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, //
2078 header_imageEl : false,
2080 layoutCls : function()
2084 Roo.log(this.margin_bottom.length);
2085 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2086 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2088 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2089 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2091 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2092 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2096 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2097 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2098 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2102 // more generic support?
2110 // Roo.log("Call onRender: " + this.xtype);
2111 /* We are looking at something like this.
2113 <img src="..." class="card-img-top" alt="...">
2114 <div class="card-body">
2115 <h5 class="card-title">Card title</h5>
2116 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2118 >> this bit is really the body...
2119 <div> << we will ad dthis in hopefully it will not break shit.
2121 ** card text does not actually have any styling...
2123 <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>
2126 <a href="#" class="card-link">Card link</a>
2129 <div class="card-footer">
2130 <small class="text-muted">Last updated 3 mins ago</small>
2134 getAutoCreate : function(){
2142 if (this.weight.length && this.weight != 'light') {
2143 cfg.cls += ' text-white';
2145 cfg.cls += ' text-dark'; // need as it's nested..
2147 if (this.weight.length) {
2148 cfg.cls += ' bg-' + this.weight;
2151 cfg.cls += ' ' + this.layoutCls();
2154 var hdr_ctr = false;
2155 if (this.header.length) {
2157 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2158 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2172 if (this.collapsable) {
2175 cls : 'd-block user-select-none',
2179 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2184 hdr.cn.push(hdr_ctr);
2189 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2194 if (this.header_image.length) {
2197 cls : 'card-img-top',
2198 src: this.header_image // escape?
2203 cls : 'card-img-top d-none'
2209 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2213 if (this.collapsable || this.rotateable) {
2216 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2223 if (this.title.length) {
2227 src: this.title // escape?
2231 if (this.subtitle.length) {
2235 src: this.subtitle // escape?
2241 cls : 'roo-card-body-ctr'
2244 if (this.html.length) {
2250 // fixme ? handle objects?
2252 if (this.footer.length) {
2255 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2260 cfg.cn.push({cls : 'card-footer d-none'});
2269 getCardHeader : function()
2271 var ret = this.el.select('.card-header',true).first();
2272 if (ret.hasClass('d-none')) {
2273 ret.removeClass('d-none');
2278 getCardFooter : function()
2280 var ret = this.el.select('.card-footer',true).first();
2281 if (ret.hasClass('d-none')) {
2282 ret.removeClass('d-none');
2287 getCardImageTop : function()
2289 var ret = this.header_imageEl;
2290 if (ret.hasClass('d-none')) {
2291 ret.removeClass('d-none');
2297 getChildContainer : function()
2303 return this.el.select('.roo-card-body-ctr',true).first();
2306 initEvents: function()
2308 this.bodyEl = this.el.select('.card-body',true).first();
2309 this.containerEl = this.getChildContainer();
2311 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2312 containerScroll: true,
2313 ddGroup: this.drag_group || 'default_card_drag_group'
2315 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2317 if (this.dropable) {
2318 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2319 containerScroll: true,
2320 ddGroup: this.drop_group || 'default_card_drag_group'
2322 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2323 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2324 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2325 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2326 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2329 if (this.collapsable) {
2330 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2332 if (this.rotateable) {
2333 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2335 this.collapsableEl = this.el.select('.roo-collapsable').first();
2337 this.footerEl = this.el.select('.card-footer').first();
2338 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2339 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2340 this.headerEl = this.el.select('.card-header',true).first();
2343 this.el.addClass('roo-card-rotated');
2344 this.fireEvent('rotate', this, true);
2346 this.header_imageEl = this.el.select('.card-img-top',true).first();
2347 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2350 getDragData : function(e)
2352 var target = this.getEl();
2354 //this.handleSelection(e);
2359 nodes: this.getEl(),
2364 dragData.ddel = target.dom ; // the div element
2365 Roo.log(target.getWidth( ));
2366 dragData.ddel.style.width = target.getWidth() + 'px';
2373 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2374 * whole Element becomes the target, and this causes the drop gesture to append.
2376 getTargetFromEvent : function(e, dragged_card_el)
2378 var target = e.getTarget();
2379 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2380 target = target.parentNode;
2391 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2392 // see if target is one of the 'cards'...
2395 //Roo.log(this.items.length);
2398 var last_card_n = 0;
2400 for (var i = 0;i< this.items.length;i++) {
2402 if (!this.items[i].el.hasClass('card')) {
2405 pos = this.getDropPoint(e, this.items[i].el.dom);
2407 cards_len = ret.cards.length;
2408 //Roo.log(this.items[i].el.dom.id);
2409 ret.cards.push(this.items[i]);
2411 if (ret.card_n < 0 && pos == 'above') {
2412 ret.position = cards_len > 0 ? 'below' : pos;
2413 ret.items_n = i > 0 ? i - 1 : 0;
2414 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2415 ret.card = ret.cards[ret.card_n];
2418 if (!ret.cards.length) {
2420 ret.position = 'below';
2424 // could not find a card.. stick it at the end..
2425 if (ret.card_n < 0) {
2426 ret.card_n = last_card_n;
2427 ret.card = ret.cards[last_card_n];
2428 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2429 ret.position = 'below';
2432 if (this.items[ret.items_n].el == dragged_card_el) {
2436 if (ret.position == 'below') {
2437 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2439 if (card_after && card_after.el == dragged_card_el) {
2446 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2448 if (card_before && card_before.el == dragged_card_el) {
2455 onNodeEnter : function(n, dd, e, data){
2458 onNodeOver : function(n, dd, e, data)
2461 var target_info = this.getTargetFromEvent(e,data.source.el);
2462 if (target_info === false) {
2463 this.dropPlaceHolder('hide');
2466 Roo.log(['getTargetFromEvent', target_info ]);
2469 this.dropPlaceHolder('show', target_info,data);
2473 onNodeOut : function(n, dd, e, data){
2474 this.dropPlaceHolder('hide');
2477 onNodeDrop : function(n, dd, e, data)
2480 // call drop - return false if
2482 // this could actually fail - if the Network drops..
2483 // we will ignore this at present..- client should probably reload
2484 // the whole set of cards if stuff like that fails.
2487 var info = this.getTargetFromEvent(e,data.source.el);
2488 if (info === false) {
2491 this.dropPlaceHolder('hide');
2497 this.acceptCard(data.source, info.position, info.card, info.items_n);
2501 firstChildCard : function()
2503 for (var i = 0;i< this.items.length;i++) {
2505 if (!this.items[i].el.hasClass('card')) {
2508 return this.items[i];
2510 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2515 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2517 acceptCard : function(move_card, position, next_to_card )
2519 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2523 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2525 move_card.parent().removeCard(move_card);
2528 var dom = move_card.el.dom;
2529 dom.style.width = ''; // clear with - which is set by drag.
2531 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2532 var cardel = next_to_card.el.dom;
2534 if (position == 'above' ) {
2535 cardel.parentNode.insertBefore(dom, cardel);
2536 } else if (cardel.nextSibling) {
2537 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2539 cardel.parentNode.append(dom);
2542 // card container???
2543 this.containerEl.dom.append(dom);
2546 //FIXME HANDLE card = true
2548 // add this to the correct place in items.
2550 // remove Card from items.
2553 if (this.items.length) {
2555 //Roo.log([info.items_n, info.position, this.items.length]);
2556 for (var i =0; i < this.items.length; i++) {
2557 if (i == to_items_n && position == 'above') {
2558 nitems.push(move_card);
2560 nitems.push(this.items[i]);
2561 if (i == to_items_n && position == 'below') {
2562 nitems.push(move_card);
2565 this.items = nitems;
2566 Roo.log(this.items);
2568 this.items.push(move_card);
2571 move_card.parentId = this.id;
2577 removeCard : function(c)
2579 this.items = this.items.filter(function(e) { return e != c });
2582 dom.parentNode.removeChild(dom);
2583 dom.style.width = ''; // clear with - which is set by drag.
2588 /** Decide whether to drop above or below a View node. */
2589 getDropPoint : function(e, n, dd)
2594 if (n == this.containerEl.dom) {
2597 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2598 var c = t + (b - t) / 2;
2599 var y = Roo.lib.Event.getPageY(e);
2606 onToggleCollapse : function(e)
2608 if (this.collapsed) {
2609 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2610 this.collapsableEl.addClass('show');
2611 this.collapsed = false;
2614 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2615 this.collapsableEl.removeClass('show');
2616 this.collapsed = true;
2621 onToggleRotate : function(e)
2623 this.collapsableEl.removeClass('show');
2624 this.footerEl.removeClass('d-none');
2625 this.el.removeClass('roo-card-rotated');
2626 this.el.removeClass('d-none');
2629 this.collapsableEl.addClass('show');
2630 this.rotated = false;
2631 this.fireEvent('rotate', this, this.rotated);
2634 this.el.addClass('roo-card-rotated');
2635 this.footerEl.addClass('d-none');
2636 this.el.select('.roo-collapsable').removeClass('show');
2638 this.rotated = true;
2639 this.fireEvent('rotate', this, this.rotated);
2643 dropPlaceHolder: function (action, info, data)
2645 if (this.dropEl === false) {
2646 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2650 this.dropEl.removeClass(['d-none', 'd-block']);
2651 if (action == 'hide') {
2653 this.dropEl.addClass('d-none');
2656 // FIXME - info.card == true!!!
2657 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2659 if (info.card !== true) {
2660 var cardel = info.card.el.dom;
2662 if (info.position == 'above') {
2663 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2664 } else if (cardel.nextSibling) {
2665 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2667 cardel.parentNode.append(this.dropEl.dom);
2670 // card container???
2671 this.containerEl.dom.append(this.dropEl.dom);
2674 this.dropEl.addClass('d-block roo-card-dropzone');
2676 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2683 setHeaderText: function(html)
2686 if (this.headerContainerEl) {
2687 this.headerContainerEl.dom.innerHTML = html;
2690 onHeaderImageLoad : function(ev, he)
2692 if (!this.header_image_fit_square) {
2696 var hw = he.naturalHeight / he.naturalWidth;
2699 //var w = he.dom.naturalWidth;
2701 Roo.get(he).setX( 0 );
2703 Roo.get(he).setSize( ww * (1/hw), ww);
2704 Roo.get(he).setX( (ww - (ww * (1/hw)))/ 2);
2715 * Card header - holder for the card header elements.
2720 * @class Roo.bootstrap.CardHeader
2721 * @extends Roo.bootstrap.Element
2722 * Bootstrap CardHeader class
2724 * Create a new Card Header - that you can embed children into
2725 * @param {Object} config The config object
2728 Roo.bootstrap.CardHeader = function(config){
2729 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2732 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2735 container_method : 'getCardHeader'
2748 * Card footer - holder for the card footer elements.
2753 * @class Roo.bootstrap.CardFooter
2754 * @extends Roo.bootstrap.Element
2755 * Bootstrap CardFooter class
2757 * Create a new Card Footer - that you can embed children into
2758 * @param {Object} config The config object
2761 Roo.bootstrap.CardFooter = function(config){
2762 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2765 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2768 container_method : 'getCardFooter'
2781 * Card header - holder for the card header elements.
2786 * @class Roo.bootstrap.CardImageTop
2787 * @extends Roo.bootstrap.Element
2788 * Bootstrap CardImageTop class
2790 * Create a new Card Image Top container
2791 * @param {Object} config The config object
2794 Roo.bootstrap.CardImageTop = function(config){
2795 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2798 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2801 container_method : 'getCardImageTop'
2819 * @class Roo.bootstrap.Img
2820 * @extends Roo.bootstrap.Component
2821 * Bootstrap Img class
2822 * @cfg {Boolean} imgResponsive false | true
2823 * @cfg {String} border rounded | circle | thumbnail
2824 * @cfg {String} src image source
2825 * @cfg {String} alt image alternative text
2826 * @cfg {String} href a tag href
2827 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2828 * @cfg {String} xsUrl xs image source
2829 * @cfg {String} smUrl sm image source
2830 * @cfg {String} mdUrl md image source
2831 * @cfg {String} lgUrl lg image source
2834 * Create a new Input
2835 * @param {Object} config The config object
2838 Roo.bootstrap.Img = function(config){
2839 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2845 * The img click event for the img.
2846 * @param {Roo.EventObject} e
2852 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2854 imgResponsive: true,
2864 getAutoCreate : function()
2866 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2867 return this.createSingleImg();
2872 cls: 'roo-image-responsive-group',
2877 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2879 if(!_this[size + 'Url']){
2885 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2886 html: _this.html || cfg.html,
2887 src: _this[size + 'Url']
2890 img.cls += ' roo-image-responsive-' + size;
2892 var s = ['xs', 'sm', 'md', 'lg'];
2894 s.splice(s.indexOf(size), 1);
2896 Roo.each(s, function(ss){
2897 img.cls += ' hidden-' + ss;
2900 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2901 cfg.cls += ' img-' + _this.border;
2905 cfg.alt = _this.alt;
2918 a.target = _this.target;
2922 cfg.cn.push((_this.href) ? a : img);
2929 createSingleImg : function()
2933 cls: (this.imgResponsive) ? 'img-responsive' : '',
2935 src : 'about:blank' // just incase src get's set to undefined?!?
2938 cfg.html = this.html || cfg.html;
2940 cfg.src = this.src || cfg.src;
2942 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2943 cfg.cls += ' img-' + this.border;
2960 a.target = this.target;
2965 return (this.href) ? a : cfg;
2968 initEvents: function()
2971 this.el.on('click', this.onClick, this);
2976 onClick : function(e)
2978 Roo.log('img onclick');
2979 this.fireEvent('click', this, e);
2982 * Sets the url of the image - used to update it
2983 * @param {String} url the url of the image
2986 setSrc : function(url)
2990 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2991 this.el.dom.src = url;
2995 this.el.select('img', true).first().dom.src = url;
3011 * @class Roo.bootstrap.Link
3012 * @extends Roo.bootstrap.Component
3013 * Bootstrap Link Class
3014 * @cfg {String} alt image alternative text
3015 * @cfg {String} href a tag href
3016 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3017 * @cfg {String} html the content of the link.
3018 * @cfg {String} anchor name for the anchor link
3019 * @cfg {String} fa - favicon
3021 * @cfg {Boolean} preventDefault (true | false) default false
3025 * Create a new Input
3026 * @param {Object} config The config object
3029 Roo.bootstrap.Link = function(config){
3030 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3036 * The img click event for the img.
3037 * @param {Roo.EventObject} e
3043 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3047 preventDefault: false,
3053 getAutoCreate : function()
3055 var html = this.html || '';
3057 if (this.fa !== false) {
3058 html = '<i class="fa fa-' + this.fa + '"></i>';
3063 // anchor's do not require html/href...
3064 if (this.anchor === false) {
3066 cfg.href = this.href || '#';
3068 cfg.name = this.anchor;
3069 if (this.html !== false || this.fa !== false) {
3072 if (this.href !== false) {
3073 cfg.href = this.href;
3077 if(this.alt !== false){
3082 if(this.target !== false) {
3083 cfg.target = this.target;
3089 initEvents: function() {
3091 if(!this.href || this.preventDefault){
3092 this.el.on('click', this.onClick, this);
3096 onClick : function(e)
3098 if(this.preventDefault){
3101 //Roo.log('img onclick');
3102 this.fireEvent('click', this, e);
3115 * @class Roo.bootstrap.Header
3116 * @extends Roo.bootstrap.Component
3117 * Bootstrap Header class
3118 * @cfg {String} html content of header
3119 * @cfg {Number} level (1|2|3|4|5|6) default 1
3122 * Create a new Header
3123 * @param {Object} config The config object
3127 Roo.bootstrap.Header = function(config){
3128 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3131 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3139 getAutoCreate : function(){
3144 tag: 'h' + (1 *this.level),
3145 html: this.html || ''
3157 * Ext JS Library 1.1.1
3158 * Copyright(c) 2006-2007, Ext JS, LLC.
3160 * Originally Released Under LGPL - original licence link has changed is not relivant.
3163 * <script type="text/javascript">
3167 * @class Roo.bootstrap.MenuMgr
3168 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3171 Roo.bootstrap.MenuMgr = function(){
3172 var menus, active, groups = {}, attached = false, lastShow = new Date();
3174 // private - called when first menu is created
3177 active = new Roo.util.MixedCollection();
3178 Roo.get(document).addKeyListener(27, function(){
3179 if(active.length > 0){
3187 if(active && active.length > 0){
3188 var c = active.clone();
3198 if(active.length < 1){
3199 Roo.get(document).un("mouseup", onMouseDown);
3207 var last = active.last();
3208 lastShow = new Date();
3211 Roo.get(document).on("mouseup", onMouseDown);
3216 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3217 m.parentMenu.activeChild = m;
3218 }else if(last && last.isVisible()){
3219 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3224 function onBeforeHide(m){
3226 m.activeChild.hide();
3228 if(m.autoHideTimer){
3229 clearTimeout(m.autoHideTimer);
3230 delete m.autoHideTimer;
3235 function onBeforeShow(m){
3236 var pm = m.parentMenu;
3237 if(!pm && !m.allowOtherMenus){
3239 }else if(pm && pm.activeChild && active != m){
3240 pm.activeChild.hide();
3244 // private this should really trigger on mouseup..
3245 function onMouseDown(e){
3246 Roo.log("on Mouse Up");
3248 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3249 Roo.log("MenuManager hideAll");
3258 function onBeforeCheck(mi, state){
3260 var g = groups[mi.group];
3261 for(var i = 0, l = g.length; i < l; i++){
3263 g[i].setChecked(false);
3272 * Hides all menus that are currently visible
3274 hideAll : function(){
3279 register : function(menu){
3283 menus[menu.id] = menu;
3284 menu.on("beforehide", onBeforeHide);
3285 menu.on("hide", onHide);
3286 menu.on("beforeshow", onBeforeShow);
3287 menu.on("show", onShow);
3289 if(g && menu.events["checkchange"]){
3293 groups[g].push(menu);
3294 menu.on("checkchange", onCheck);
3299 * Returns a {@link Roo.menu.Menu} object
3300 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3301 * be used to generate and return a new Menu instance.
3303 get : function(menu){
3304 if(typeof menu == "string"){ // menu id
3306 }else if(menu.events){ // menu instance
3309 /*else if(typeof menu.length == 'number'){ // array of menu items?
3310 return new Roo.bootstrap.Menu({items:menu});
3311 }else{ // otherwise, must be a config
3312 return new Roo.bootstrap.Menu(menu);
3319 unregister : function(menu){
3320 delete menus[menu.id];
3321 menu.un("beforehide", onBeforeHide);
3322 menu.un("hide", onHide);
3323 menu.un("beforeshow", onBeforeShow);
3324 menu.un("show", onShow);
3326 if(g && menu.events["checkchange"]){
3327 groups[g].remove(menu);
3328 menu.un("checkchange", onCheck);
3333 registerCheckable : function(menuItem){
3334 var g = menuItem.group;
3339 groups[g].push(menuItem);
3340 menuItem.on("beforecheckchange", onBeforeCheck);
3345 unregisterCheckable : function(menuItem){
3346 var g = menuItem.group;
3348 groups[g].remove(menuItem);
3349 menuItem.un("beforecheckchange", onBeforeCheck);
3361 * @class Roo.bootstrap.Menu
3362 * @extends Roo.bootstrap.Component
3363 * Bootstrap Menu class - container for MenuItems
3364 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3365 * @cfg {bool} hidden if the menu should be hidden when rendered.
3366 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3367 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3371 * @param {Object} config The config object
3375 Roo.bootstrap.Menu = function(config){
3376 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3377 if (this.registerMenu && this.type != 'treeview') {
3378 Roo.bootstrap.MenuMgr.register(this);
3385 * Fires before this menu is displayed (return false to block)
3386 * @param {Roo.menu.Menu} this
3391 * Fires before this menu is hidden (return false to block)
3392 * @param {Roo.menu.Menu} this
3397 * Fires after this menu is displayed
3398 * @param {Roo.menu.Menu} this
3403 * Fires after this menu is hidden
3404 * @param {Roo.menu.Menu} this
3409 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3410 * @param {Roo.menu.Menu} this
3411 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3412 * @param {Roo.EventObject} e
3417 * Fires when the mouse is hovering over this menu
3418 * @param {Roo.menu.Menu} this
3419 * @param {Roo.EventObject} e
3420 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3425 * Fires when the mouse exits this menu
3426 * @param {Roo.menu.Menu} this
3427 * @param {Roo.EventObject} e
3428 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3433 * Fires when a menu item contained in this menu is clicked
3434 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3435 * @param {Roo.EventObject} e
3439 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3442 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3446 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3449 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3451 registerMenu : true,
3453 menuItems :false, // stores the menu items..
3463 getChildContainer : function() {
3467 getAutoCreate : function(){
3469 //if (['right'].indexOf(this.align)!==-1) {
3470 // cfg.cn[1].cls += ' pull-right'
3476 cls : 'dropdown-menu' ,
3477 style : 'z-index:1000'
3481 if (this.type === 'submenu') {
3482 cfg.cls = 'submenu active';
3484 if (this.type === 'treeview') {
3485 cfg.cls = 'treeview-menu';
3490 initEvents : function() {
3492 // Roo.log("ADD event");
3493 // Roo.log(this.triggerEl.dom);
3495 this.triggerEl.on('click', this.onTriggerClick, this);
3497 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3500 if (this.triggerEl.hasClass('nav-item')) {
3501 // dropdown toggle on the 'a' in BS4?
3502 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3504 this.triggerEl.addClass('dropdown-toggle');
3507 this.el.on('touchstart' , this.onTouch, this);
3509 this.el.on('click' , this.onClick, this);
3511 this.el.on("mouseover", this.onMouseOver, this);
3512 this.el.on("mouseout", this.onMouseOut, this);
3516 findTargetItem : function(e)
3518 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3522 //Roo.log(t); Roo.log(t.id);
3524 //Roo.log(this.menuitems);
3525 return this.menuitems.get(t.id);
3527 //return this.items.get(t.menuItemId);
3533 onTouch : function(e)
3535 Roo.log("menu.onTouch");
3536 //e.stopEvent(); this make the user popdown broken
3540 onClick : function(e)
3542 Roo.log("menu.onClick");
3544 var t = this.findTargetItem(e);
3545 if(!t || t.isContainer){
3550 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3551 if(t == this.activeItem && t.shouldDeactivate(e)){
3552 this.activeItem.deactivate();
3553 delete this.activeItem;
3557 this.setActiveItem(t, true);
3565 Roo.log('pass click event');
3569 this.fireEvent("click", this, t, e);
3573 if(!t.href.length || t.href == '#'){
3574 (function() { _this.hide(); }).defer(100);
3579 onMouseOver : function(e){
3580 var t = this.findTargetItem(e);
3583 // if(t.canActivate && !t.disabled){
3584 // this.setActiveItem(t, true);
3588 this.fireEvent("mouseover", this, e, t);
3590 isVisible : function(){
3591 return !this.hidden;
3593 onMouseOut : function(e){
3594 var t = this.findTargetItem(e);
3597 // if(t == this.activeItem && t.shouldDeactivate(e)){
3598 // this.activeItem.deactivate();
3599 // delete this.activeItem;
3602 this.fireEvent("mouseout", this, e, t);
3607 * Displays this menu relative to another element
3608 * @param {String/HTMLElement/Roo.Element} element The element to align to
3609 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3610 * the element (defaults to this.defaultAlign)
3611 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3613 show : function(el, pos, parentMenu)
3615 if (false === this.fireEvent("beforeshow", this)) {
3616 Roo.log("show canceled");
3619 this.parentMenu = parentMenu;
3624 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3627 * Displays this menu at a specific xy position
3628 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3629 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3631 showAt : function(xy, parentMenu, /* private: */_e){
3632 this.parentMenu = parentMenu;
3637 this.fireEvent("beforeshow", this);
3638 //xy = this.el.adjustForConstraints(xy);
3642 this.hideMenuItems();
3643 this.hidden = false;
3644 this.triggerEl.addClass('open');
3645 this.el.addClass('show');
3647 // reassign x when hitting right
3648 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3649 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3652 // reassign y when hitting bottom
3653 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3654 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3657 // but the list may align on trigger left or trigger top... should it be a properity?
3659 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3664 this.fireEvent("show", this);
3670 this.doFocus.defer(50, this);
3674 doFocus : function(){
3676 this.focusEl.focus();
3681 * Hides this menu and optionally all parent menus
3682 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3684 hide : function(deep)
3686 if (false === this.fireEvent("beforehide", this)) {
3687 Roo.log("hide canceled");
3690 this.hideMenuItems();
3691 if(this.el && this.isVisible()){
3693 if(this.activeItem){
3694 this.activeItem.deactivate();
3695 this.activeItem = null;
3697 this.triggerEl.removeClass('open');;
3698 this.el.removeClass('show');
3700 this.fireEvent("hide", this);
3702 if(deep === true && this.parentMenu){
3703 this.parentMenu.hide(true);
3707 onTriggerClick : function(e)
3709 Roo.log('trigger click');
3711 var target = e.getTarget();
3713 Roo.log(target.nodeName.toLowerCase());
3715 if(target.nodeName.toLowerCase() === 'i'){
3721 onTriggerPress : function(e)
3723 Roo.log('trigger press');
3724 //Roo.log(e.getTarget());
3725 // Roo.log(this.triggerEl.dom);
3727 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3728 var pel = Roo.get(e.getTarget());
3729 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3730 Roo.log('is treeview or dropdown?');
3734 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3738 if (this.isVisible()) {
3743 this.show(this.triggerEl, '?', false);
3746 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3753 hideMenuItems : function()
3755 Roo.log("hide Menu Items");
3760 this.el.select('.open',true).each(function(aa) {
3762 aa.removeClass('open');
3766 addxtypeChild : function (tree, cntr) {
3767 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3769 this.menuitems.add(comp);
3781 this.getEl().dom.innerHTML = '';
3782 this.menuitems.clear();
3796 * @class Roo.bootstrap.MenuItem
3797 * @extends Roo.bootstrap.Component
3798 * Bootstrap MenuItem class
3799 * @cfg {String} html the menu label
3800 * @cfg {String} href the link
3801 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3802 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3803 * @cfg {Boolean} active used on sidebars to highlight active itesm
3804 * @cfg {String} fa favicon to show on left of menu item.
3805 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3809 * Create a new MenuItem
3810 * @param {Object} config The config object
3814 Roo.bootstrap.MenuItem = function(config){
3815 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3820 * The raw click event for the entire grid.
3821 * @param {Roo.bootstrap.MenuItem} this
3822 * @param {Roo.EventObject} e
3828 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3832 preventDefault: false,
3833 isContainer : false,
3837 getAutoCreate : function(){
3839 if(this.isContainer){
3842 cls: 'dropdown-menu-item '
3852 cls : 'dropdown-item',
3857 if (this.fa !== false) {
3860 cls : 'fa fa-' + this.fa
3869 cls: 'dropdown-menu-item',
3872 if (this.parent().type == 'treeview') {
3873 cfg.cls = 'treeview-menu';
3876 cfg.cls += ' active';
3881 anc.href = this.href || cfg.cn[0].href ;
3882 ctag.html = this.html || cfg.cn[0].html ;
3886 initEvents: function()
3888 if (this.parent().type == 'treeview') {
3889 this.el.select('a').on('click', this.onClick, this);
3893 this.menu.parentType = this.xtype;
3894 this.menu.triggerEl = this.el;
3895 this.menu = this.addxtype(Roo.apply({}, this.menu));
3899 onClick : function(e)
3901 Roo.log('item on click ');
3903 if(this.preventDefault){
3906 //this.parent().hideMenuItems();
3908 this.fireEvent('click', this, e);
3927 * @class Roo.bootstrap.MenuSeparator
3928 * @extends Roo.bootstrap.Component
3929 * Bootstrap MenuSeparator class
3932 * Create a new MenuItem
3933 * @param {Object} config The config object
3937 Roo.bootstrap.MenuSeparator = function(config){
3938 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3941 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3943 getAutoCreate : function(){
3962 * @class Roo.bootstrap.Modal
3963 * @extends Roo.bootstrap.Component
3964 * Bootstrap Modal class
3965 * @cfg {String} title Title of dialog
3966 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3967 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3968 * @cfg {Boolean} specificTitle default false
3969 * @cfg {Array} buttons Array of buttons or standard button set..
3970 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3971 * @cfg {Boolean} animate default true
3972 * @cfg {Boolean} allow_close default true
3973 * @cfg {Boolean} fitwindow default false
3974 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3975 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3976 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3977 * @cfg {String} size (sm|lg|xl) default empty
3978 * @cfg {Number} max_width set the max width of modal
3979 * @cfg {Boolean} editableTitle can the title be edited
3984 * Create a new Modal Dialog
3985 * @param {Object} config The config object
3988 Roo.bootstrap.Modal = function(config){
3989 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3994 * The raw btnclick event for the button
3995 * @param {Roo.EventObject} e
4000 * Fire when dialog resize
4001 * @param {Roo.bootstrap.Modal} this
4002 * @param {Roo.EventObject} e
4006 * @event titlechanged
4007 * Fire when the editable title has been changed
4008 * @param {Roo.bootstrap.Modal} this
4009 * @param {Roo.EventObject} value
4011 "titlechanged" : true
4014 this.buttons = this.buttons || [];
4017 this.tmpl = Roo.factory(this.tmpl);
4022 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4024 title : 'test dialog',
4034 specificTitle: false,
4036 buttonPosition: 'right',
4058 editableTitle : false,
4060 onRender : function(ct, position)
4062 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4065 var cfg = Roo.apply({}, this.getAutoCreate());
4068 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4070 //if (!cfg.name.length) {
4074 cfg.cls += ' ' + this.cls;
4077 cfg.style = this.style;
4079 this.el = Roo.get(document.body).createChild(cfg, position);
4081 //var type = this.el.dom.type;
4084 if(this.tabIndex !== undefined){
4085 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4088 this.dialogEl = this.el.select('.modal-dialog',true).first();
4089 this.bodyEl = this.el.select('.modal-body',true).first();
4090 this.closeEl = this.el.select('.modal-header .close', true).first();
4091 this.headerEl = this.el.select('.modal-header',true).first();
4092 this.titleEl = this.el.select('.modal-title',true).first();
4093 this.footerEl = this.el.select('.modal-footer',true).first();
4095 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4097 //this.el.addClass("x-dlg-modal");
4099 if (this.buttons.length) {
4100 Roo.each(this.buttons, function(bb) {
4101 var b = Roo.apply({}, bb);
4102 b.xns = b.xns || Roo.bootstrap;
4103 b.xtype = b.xtype || 'Button';
4104 if (typeof(b.listeners) == 'undefined') {
4105 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4108 var btn = Roo.factory(b);
4110 btn.render(this.getButtonContainer());
4114 // render the children.
4117 if(typeof(this.items) != 'undefined'){
4118 var items = this.items;
4121 for(var i =0;i < items.length;i++) {
4122 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4126 this.items = nitems;
4128 // where are these used - they used to be body/close/footer
4132 //this.el.addClass([this.fieldClass, this.cls]);
4136 getAutoCreate : function()
4138 // we will default to modal-body-overflow - might need to remove or make optional later.
4140 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4141 html : this.html || ''
4146 cls : 'modal-title',
4150 if(this.specificTitle){ // WTF is this?
4155 if (this.allow_close && Roo.bootstrap.version == 3) {
4165 if (this.editableTitle) {
4167 cls: 'form-control roo-editable-title d-none',
4173 if (this.allow_close && Roo.bootstrap.version == 4) {
4183 if(this.size.length){
4184 size = 'modal-' + this.size;
4187 var footer = Roo.bootstrap.version == 3 ?
4189 cls : 'modal-footer',
4193 cls: 'btn-' + this.buttonPosition
4198 { // BS4 uses mr-auto on left buttons....
4199 cls : 'modal-footer'
4210 cls: "modal-dialog " + size,
4213 cls : "modal-content",
4216 cls : 'modal-header',
4231 modal.cls += ' fade';
4237 getChildContainer : function() {
4242 getButtonContainer : function() {
4244 return Roo.bootstrap.version == 4 ?
4245 this.el.select('.modal-footer',true).first()
4246 : this.el.select('.modal-footer div',true).first();
4249 initEvents : function()
4251 if (this.allow_close) {
4252 this.closeEl.on('click', this.hide, this);
4254 Roo.EventManager.onWindowResize(this.resize, this, true);
4255 if (this.editableTitle) {
4256 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4257 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4258 this.headerEditEl.on('keyup', function(e) {
4259 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4260 this.toggleHeaderInput(false)
4263 this.headerEditEl.on('blur', function(e) {
4264 this.toggleHeaderInput(false)
4273 this.maskEl.setSize(
4274 Roo.lib.Dom.getViewWidth(true),
4275 Roo.lib.Dom.getViewHeight(true)
4278 if (this.fitwindow) {
4280 this.dialogEl.setStyle( { 'max-width' : '100%' });
4282 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4283 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4288 if(this.max_width !== 0) {
4290 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4293 this.setSize(w, this.height);
4297 if(this.max_height) {
4298 this.setSize(w,Math.min(
4300 Roo.lib.Dom.getViewportHeight(true) - 60
4306 if(!this.fit_content) {
4307 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4311 this.setSize(w, Math.min(
4313 this.headerEl.getHeight() +
4314 this.footerEl.getHeight() +
4315 this.getChildHeight(this.bodyEl.dom.childNodes),
4316 Roo.lib.Dom.getViewportHeight(true) - 60)
4322 setSize : function(w,h)
4333 if (!this.rendered) {
4336 this.toggleHeaderInput(false);
4337 //this.el.setStyle('display', 'block');
4338 this.el.removeClass('hideing');
4339 this.el.dom.style.display='block';
4341 Roo.get(document.body).addClass('modal-open');
4343 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4346 this.el.addClass('show');
4347 this.el.addClass('in');
4350 this.el.addClass('show');
4351 this.el.addClass('in');
4354 // not sure how we can show data in here..
4356 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4359 Roo.get(document.body).addClass("x-body-masked");
4361 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4362 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4363 this.maskEl.dom.style.display = 'block';
4364 this.maskEl.addClass('show');
4369 this.fireEvent('show', this);
4371 // set zindex here - otherwise it appears to be ignored...
4372 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4375 this.items.forEach( function(e) {
4376 e.layout ? e.layout() : false;
4384 if(this.fireEvent("beforehide", this) !== false){
4386 this.maskEl.removeClass('show');
4388 this.maskEl.dom.style.display = '';
4389 Roo.get(document.body).removeClass("x-body-masked");
4390 this.el.removeClass('in');
4391 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4393 if(this.animate){ // why
4394 this.el.addClass('hideing');
4395 this.el.removeClass('show');
4397 if (!this.el.hasClass('hideing')) {
4398 return; // it's been shown again...
4401 this.el.dom.style.display='';
4403 Roo.get(document.body).removeClass('modal-open');
4404 this.el.removeClass('hideing');
4408 this.el.removeClass('show');
4409 this.el.dom.style.display='';
4410 Roo.get(document.body).removeClass('modal-open');
4413 this.fireEvent('hide', this);
4416 isVisible : function()
4419 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4423 addButton : function(str, cb)
4427 var b = Roo.apply({}, { html : str } );
4428 b.xns = b.xns || Roo.bootstrap;
4429 b.xtype = b.xtype || 'Button';
4430 if (typeof(b.listeners) == 'undefined') {
4431 b.listeners = { click : cb.createDelegate(this) };
4434 var btn = Roo.factory(b);
4436 btn.render(this.getButtonContainer());
4442 setDefaultButton : function(btn)
4444 //this.el.select('.modal-footer').()
4447 resizeTo: function(w,h)
4449 this.dialogEl.setWidth(w);
4451 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4453 this.bodyEl.setHeight(h - diff);
4455 this.fireEvent('resize', this);
4458 setContentSize : function(w, h)
4462 onButtonClick: function(btn,e)
4465 this.fireEvent('btnclick', btn.name, e);
4468 * Set the title of the Dialog
4469 * @param {String} str new Title
4471 setTitle: function(str) {
4472 this.titleEl.dom.innerHTML = str;
4476 * Set the body of the Dialog
4477 * @param {String} str new Title
4479 setBody: function(str) {
4480 this.bodyEl.dom.innerHTML = str;
4483 * Set the body of the Dialog using the template
4484 * @param {Obj} data - apply this data to the template and replace the body contents.
4486 applyBody: function(obj)
4489 Roo.log("Error - using apply Body without a template");
4492 this.tmpl.overwrite(this.bodyEl, obj);
4495 getChildHeight : function(child_nodes)
4499 child_nodes.length == 0
4504 var child_height = 0;
4506 for(var i = 0; i < child_nodes.length; i++) {
4509 * for modal with tabs...
4510 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4512 var layout_childs = child_nodes[i].childNodes;
4514 for(var j = 0; j < layout_childs.length; j++) {
4516 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4518 var layout_body_childs = layout_childs[j].childNodes;
4520 for(var k = 0; k < layout_body_childs.length; k++) {
4522 if(layout_body_childs[k].classList.contains('navbar')) {
4523 child_height += layout_body_childs[k].offsetHeight;
4527 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4529 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4531 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4533 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4534 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4549 child_height += child_nodes[i].offsetHeight;
4550 // Roo.log(child_nodes[i].offsetHeight);
4553 return child_height;
4555 toggleHeaderInput : function(is_edit)
4557 if (!this.editableTitle) {
4558 return; // not editable.
4560 if (is_edit && this.is_header_editing) {
4561 return; // already editing..
4565 this.headerEditEl.dom.value = this.title;
4566 this.headerEditEl.removeClass('d-none');
4567 this.headerEditEl.dom.focus();
4568 this.titleEl.addClass('d-none');
4570 this.is_header_editing = true;
4573 // flip back to not editing.
4574 this.title = this.headerEditEl.dom.value;
4575 this.headerEditEl.addClass('d-none');
4576 this.titleEl.removeClass('d-none');
4577 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4578 this.is_header_editing = false;
4579 this.fireEvent('titlechanged', this, this.title);
4588 Roo.apply(Roo.bootstrap.Modal, {
4590 * Button config that displays a single OK button
4599 * Button config that displays Yes and No buttons
4615 * Button config that displays OK and Cancel buttons
4630 * Button config that displays Yes, No and Cancel buttons
4655 * messagebox - can be used as a replace
4659 * @class Roo.MessageBox
4660 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4664 Roo.Msg.alert('Status', 'Changes saved successfully.');
4666 // Prompt for user data:
4667 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4669 // process text value...
4673 // Show a dialog using config options:
4675 title:'Save Changes?',
4676 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4677 buttons: Roo.Msg.YESNOCANCEL,
4684 Roo.bootstrap.MessageBox = function(){
4685 var dlg, opt, mask, waitTimer;
4686 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4687 var buttons, activeTextEl, bwidth;
4691 var handleButton = function(button){
4693 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4697 var handleHide = function(){
4699 dlg.el.removeClass(opt.cls);
4702 // Roo.TaskMgr.stop(waitTimer);
4703 // waitTimer = null;
4708 var updateButtons = function(b){
4711 buttons["ok"].hide();
4712 buttons["cancel"].hide();
4713 buttons["yes"].hide();
4714 buttons["no"].hide();
4715 dlg.footerEl.hide();
4719 dlg.footerEl.show();
4720 for(var k in buttons){
4721 if(typeof buttons[k] != "function"){
4724 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4725 width += buttons[k].el.getWidth()+15;
4735 var handleEsc = function(d, k, e){
4736 if(opt && opt.closable !== false){
4746 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4747 * @return {Roo.BasicDialog} The BasicDialog element
4749 getDialog : function(){
4751 dlg = new Roo.bootstrap.Modal( {
4754 //constraintoviewport:false,
4756 //collapsible : false,
4761 //buttonAlign:"center",
4762 closeClick : function(){
4763 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4766 handleButton("cancel");
4771 dlg.on("hide", handleHide);
4773 //dlg.addKeyListener(27, handleEsc);
4775 this.buttons = buttons;
4776 var bt = this.buttonText;
4777 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4778 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4779 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4780 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4782 bodyEl = dlg.bodyEl.createChild({
4784 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4785 '<textarea class="roo-mb-textarea"></textarea>' +
4786 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4788 msgEl = bodyEl.dom.firstChild;
4789 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4790 textboxEl.enableDisplayMode();
4791 textboxEl.addKeyListener([10,13], function(){
4792 if(dlg.isVisible() && opt && opt.buttons){
4795 }else if(opt.buttons.yes){
4796 handleButton("yes");
4800 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4801 textareaEl.enableDisplayMode();
4802 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4803 progressEl.enableDisplayMode();
4805 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4806 var pf = progressEl.dom.firstChild;
4808 pp = Roo.get(pf.firstChild);
4809 pp.setHeight(pf.offsetHeight);
4817 * Updates the message box body text
4818 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4819 * the XHTML-compliant non-breaking space character '&#160;')
4820 * @return {Roo.MessageBox} This message box
4822 updateText : function(text)
4824 if(!dlg.isVisible() && !opt.width){
4825 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4826 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4828 msgEl.innerHTML = text || ' ';
4830 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4831 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4833 Math.min(opt.width || cw , this.maxWidth),
4834 Math.max(opt.minWidth || this.minWidth, bwidth)
4837 activeTextEl.setWidth(w);
4839 if(dlg.isVisible()){
4840 dlg.fixedcenter = false;
4842 // to big, make it scroll. = But as usual stupid IE does not support
4845 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4846 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4847 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4849 bodyEl.dom.style.height = '';
4850 bodyEl.dom.style.overflowY = '';
4853 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4855 bodyEl.dom.style.overflowX = '';
4858 dlg.setContentSize(w, bodyEl.getHeight());
4859 if(dlg.isVisible()){
4860 dlg.fixedcenter = true;
4866 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4867 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4868 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4869 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4870 * @return {Roo.MessageBox} This message box
4872 updateProgress : function(value, text){
4874 this.updateText(text);
4877 if (pp) { // weird bug on my firefox - for some reason this is not defined
4878 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4879 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4885 * Returns true if the message box is currently displayed
4886 * @return {Boolean} True if the message box is visible, else false
4888 isVisible : function(){
4889 return dlg && dlg.isVisible();
4893 * Hides the message box if it is displayed
4896 if(this.isVisible()){
4902 * Displays a new message box, or reinitializes an existing message box, based on the config options
4903 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4904 * The following config object properties are supported:
4906 Property Type Description
4907 ---------- --------------- ------------------------------------------------------------------------------------
4908 animEl String/Element An id or Element from which the message box should animate as it opens and
4909 closes (defaults to undefined)
4910 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4911 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4912 closable Boolean False to hide the top-right close button (defaults to true). Note that
4913 progress and wait dialogs will ignore this property and always hide the
4914 close button as they can only be closed programmatically.
4915 cls String A custom CSS class to apply to the message box element
4916 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4917 displayed (defaults to 75)
4918 fn Function A callback function to execute after closing the dialog. The arguments to the
4919 function will be btn (the name of the button that was clicked, if applicable,
4920 e.g. "ok"), and text (the value of the active text field, if applicable).
4921 Progress and wait dialogs will ignore this option since they do not respond to
4922 user actions and can only be closed programmatically, so any required function
4923 should be called by the same code after it closes the dialog.
4924 icon String A CSS class that provides a background image to be used as an icon for
4925 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4926 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4927 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4928 modal Boolean False to allow user interaction with the page while the message box is
4929 displayed (defaults to true)
4930 msg String A string that will replace the existing message box body text (defaults
4931 to the XHTML-compliant non-breaking space character ' ')
4932 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4933 progress Boolean True to display a progress bar (defaults to false)
4934 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4935 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4936 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4937 title String The title text
4938 value String The string value to set into the active textbox element if displayed
4939 wait Boolean True to display a progress bar (defaults to false)
4940 width Number The width of the dialog in pixels
4947 msg: 'Please enter your address:',
4949 buttons: Roo.MessageBox.OKCANCEL,
4952 animEl: 'addAddressBtn'
4955 * @param {Object} config Configuration options
4956 * @return {Roo.MessageBox} This message box
4958 show : function(options)
4961 // this causes nightmares if you show one dialog after another
4962 // especially on callbacks..
4964 if(this.isVisible()){
4967 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4968 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4969 Roo.log("New Dialog Message:" + options.msg )
4970 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4971 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4974 var d = this.getDialog();
4976 d.setTitle(opt.title || " ");
4977 d.closeEl.setDisplayed(opt.closable !== false);
4978 activeTextEl = textboxEl;
4979 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4984 textareaEl.setHeight(typeof opt.multiline == "number" ?
4985 opt.multiline : this.defaultTextHeight);
4986 activeTextEl = textareaEl;
4995 progressEl.setDisplayed(opt.progress === true);
4997 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4999 this.updateProgress(0);
5000 activeTextEl.dom.value = opt.value || "";
5002 dlg.setDefaultButton(activeTextEl);
5004 var bs = opt.buttons;
5008 }else if(bs && bs.yes){
5009 db = buttons["yes"];
5011 dlg.setDefaultButton(db);
5013 bwidth = updateButtons(opt.buttons);
5014 this.updateText(opt.msg);
5016 d.el.addClass(opt.cls);
5018 d.proxyDrag = opt.proxyDrag === true;
5019 d.modal = opt.modal !== false;
5020 d.mask = opt.modal !== false ? mask : false;
5022 // force it to the end of the z-index stack so it gets a cursor in FF
5023 document.body.appendChild(dlg.el.dom);
5024 d.animateTarget = null;
5025 d.show(options.animEl);
5031 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5032 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5033 * and closing the message box when the process is complete.
5034 * @param {String} title The title bar text
5035 * @param {String} msg The message box body text
5036 * @return {Roo.MessageBox} This message box
5038 progress : function(title, msg){
5045 minWidth: this.minProgressWidth,
5052 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5053 * If a callback function is passed it will be called after the user clicks the button, and the
5054 * id of the button that was clicked will be passed as the only parameter to the callback
5055 * (could also be the top-right close button).
5056 * @param {String} title The title bar text
5057 * @param {String} msg The message box body text
5058 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5059 * @param {Object} scope (optional) The scope of the callback function
5060 * @return {Roo.MessageBox} This message box
5062 alert : function(title, msg, fn, scope)
5077 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5078 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5079 * You are responsible for closing the message box when the process is complete.
5080 * @param {String} msg The message box body text
5081 * @param {String} title (optional) The title bar text
5082 * @return {Roo.MessageBox} This message box
5084 wait : function(msg, title){
5095 waitTimer = Roo.TaskMgr.start({
5097 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5105 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5106 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5107 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5108 * @param {String} title The title bar text
5109 * @param {String} msg The message box body text
5110 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111 * @param {Object} scope (optional) The scope of the callback function
5112 * @return {Roo.MessageBox} This message box
5114 confirm : function(title, msg, fn, scope){
5118 buttons: this.YESNO,
5127 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5128 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5129 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5130 * (could also be the top-right close button) and the text that was entered will be passed as the two
5131 * parameters to the callback.
5132 * @param {String} title The title bar text
5133 * @param {String} msg The message box body text
5134 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5135 * @param {Object} scope (optional) The scope of the callback function
5136 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5137 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5138 * @return {Roo.MessageBox} This message box
5140 prompt : function(title, msg, fn, scope, multiline){
5144 buttons: this.OKCANCEL,
5149 multiline: multiline,
5156 * Button config that displays a single OK button
5161 * Button config that displays Yes and No buttons
5164 YESNO : {yes:true, no:true},
5166 * Button config that displays OK and Cancel buttons
5169 OKCANCEL : {ok:true, cancel:true},
5171 * Button config that displays Yes, No and Cancel buttons
5174 YESNOCANCEL : {yes:true, no:true, cancel:true},
5177 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5180 defaultTextHeight : 75,
5182 * The maximum width in pixels of the message box (defaults to 600)
5187 * The minimum width in pixels of the message box (defaults to 100)
5192 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5193 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5196 minProgressWidth : 250,
5198 * An object containing the default button text strings that can be overriden for localized language support.
5199 * Supported properties are: ok, cancel, yes and no.
5200 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5213 * Shorthand for {@link Roo.MessageBox}
5215 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5216 Roo.Msg = Roo.Msg || Roo.MessageBox;
5225 * @class Roo.bootstrap.Navbar
5226 * @extends Roo.bootstrap.Component
5227 * Bootstrap Navbar class
5230 * Create a new Navbar
5231 * @param {Object} config The config object
5235 Roo.bootstrap.Navbar = function(config){
5236 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5240 * @event beforetoggle
5241 * Fire before toggle the menu
5242 * @param {Roo.EventObject} e
5244 "beforetoggle" : true
5248 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5257 getAutoCreate : function(){
5260 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5264 initEvents :function ()
5266 //Roo.log(this.el.select('.navbar-toggle',true));
5267 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5274 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5276 var size = this.el.getSize();
5277 this.maskEl.setSize(size.width, size.height);
5278 this.maskEl.enableDisplayMode("block");
5287 getChildContainer : function()
5289 if (this.el && this.el.select('.collapse').getCount()) {
5290 return this.el.select('.collapse',true).first();
5305 onToggle : function()
5308 if(this.fireEvent('beforetoggle', this) === false){
5311 var ce = this.el.select('.navbar-collapse',true).first();
5313 if (!ce.hasClass('show')) {
5323 * Expand the navbar pulldown
5325 expand : function ()
5328 var ce = this.el.select('.navbar-collapse',true).first();
5329 if (ce.hasClass('collapsing')) {
5332 ce.dom.style.height = '';
5334 ce.addClass('in'); // old...
5335 ce.removeClass('collapse');
5336 ce.addClass('show');
5337 var h = ce.getHeight();
5339 ce.removeClass('show');
5340 // at this point we should be able to see it..
5341 ce.addClass('collapsing');
5343 ce.setHeight(0); // resize it ...
5344 ce.on('transitionend', function() {
5345 //Roo.log('done transition');
5346 ce.removeClass('collapsing');
5347 ce.addClass('show');
5348 ce.removeClass('collapse');
5350 ce.dom.style.height = '';
5351 }, this, { single: true} );
5353 ce.dom.scrollTop = 0;
5356 * Collapse the navbar pulldown
5358 collapse : function()
5360 var ce = this.el.select('.navbar-collapse',true).first();
5362 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5363 // it's collapsed or collapsing..
5366 ce.removeClass('in'); // old...
5367 ce.setHeight(ce.getHeight());
5368 ce.removeClass('show');
5369 ce.addClass('collapsing');
5371 ce.on('transitionend', function() {
5372 ce.dom.style.height = '';
5373 ce.removeClass('collapsing');
5374 ce.addClass('collapse');
5375 }, this, { single: true} );
5395 * @class Roo.bootstrap.NavSimplebar
5396 * @extends Roo.bootstrap.Navbar
5397 * Bootstrap Sidebar class
5399 * @cfg {Boolean} inverse is inverted color
5401 * @cfg {String} type (nav | pills | tabs)
5402 * @cfg {Boolean} arrangement stacked | justified
5403 * @cfg {String} align (left | right) alignment
5405 * @cfg {Boolean} main (true|false) main nav bar? default false
5406 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5408 * @cfg {String} tag (header|footer|nav|div) default is nav
5410 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5414 * Create a new Sidebar
5415 * @param {Object} config The config object
5419 Roo.bootstrap.NavSimplebar = function(config){
5420 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5423 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5439 getAutoCreate : function(){
5443 tag : this.tag || 'div',
5444 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5446 if (['light','white'].indexOf(this.weight) > -1) {
5447 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5449 cfg.cls += ' bg-' + this.weight;
5452 cfg.cls += ' navbar-inverse';
5456 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5458 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5467 cls: 'nav nav-' + this.xtype,
5473 this.type = this.type || 'nav';
5474 if (['tabs','pills'].indexOf(this.type) != -1) {
5475 cfg.cn[0].cls += ' nav-' + this.type
5479 if (this.type!=='nav') {
5480 Roo.log('nav type must be nav/tabs/pills')
5482 cfg.cn[0].cls += ' navbar-nav'
5488 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5489 cfg.cn[0].cls += ' nav-' + this.arrangement;
5493 if (this.align === 'right') {
5494 cfg.cn[0].cls += ' navbar-right';
5519 * navbar-expand-md fixed-top
5523 * @class Roo.bootstrap.NavHeaderbar
5524 * @extends Roo.bootstrap.NavSimplebar
5525 * Bootstrap Sidebar class
5527 * @cfg {String} brand what is brand
5528 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5529 * @cfg {String} brand_href href of the brand
5530 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5531 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5532 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5533 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5536 * Create a new Sidebar
5537 * @param {Object} config The config object
5541 Roo.bootstrap.NavHeaderbar = function(config){
5542 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5546 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5553 desktopCenter : false,
5556 getAutoCreate : function(){
5559 tag: this.nav || 'nav',
5560 cls: 'navbar navbar-expand-md',
5566 if (this.desktopCenter) {
5567 cn.push({cls : 'container', cn : []});
5575 cls: 'navbar-toggle navbar-toggler',
5576 'data-toggle': 'collapse',
5581 html: 'Toggle navigation'
5585 cls: 'icon-bar navbar-toggler-icon'
5598 cn.push( Roo.bootstrap.version == 4 ? btn : {
5600 cls: 'navbar-header',
5609 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5613 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5615 if (['light','white'].indexOf(this.weight) > -1) {
5616 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5618 cfg.cls += ' bg-' + this.weight;
5621 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5622 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5624 // tag can override this..
5626 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5629 if (this.brand !== '') {
5630 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5631 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5633 href: this.brand_href ? this.brand_href : '#',
5634 cls: 'navbar-brand',
5642 cfg.cls += ' main-nav';
5650 getHeaderChildContainer : function()
5652 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5653 return this.el.select('.navbar-header',true).first();
5656 return this.getChildContainer();
5659 getChildContainer : function()
5662 return this.el.select('.roo-navbar-collapse',true).first();
5667 initEvents : function()
5669 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5671 if (this.autohide) {
5676 Roo.get(document).on('scroll',function(e) {
5677 var ns = Roo.get(document).getScroll().top;
5678 var os = prevScroll;
5682 ft.removeClass('slideDown');
5683 ft.addClass('slideUp');
5686 ft.removeClass('slideUp');
5687 ft.addClass('slideDown');
5708 * @class Roo.bootstrap.NavSidebar
5709 * @extends Roo.bootstrap.Navbar
5710 * Bootstrap Sidebar class
5713 * Create a new Sidebar
5714 * @param {Object} config The config object
5718 Roo.bootstrap.NavSidebar = function(config){
5719 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5722 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5724 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5726 getAutoCreate : function(){
5731 cls: 'sidebar sidebar-nav'
5753 * @class Roo.bootstrap.NavGroup
5754 * @extends Roo.bootstrap.Component
5755 * Bootstrap NavGroup class
5756 * @cfg {String} align (left|right)
5757 * @cfg {Boolean} inverse
5758 * @cfg {String} type (nav|pills|tab) default nav
5759 * @cfg {String} navId - reference Id for navbar.
5760 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5763 * Create a new nav group
5764 * @param {Object} config The config object
5767 Roo.bootstrap.NavGroup = function(config){
5768 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5771 Roo.bootstrap.NavGroup.register(this);
5775 * Fires when the active item changes
5776 * @param {Roo.bootstrap.NavGroup} this
5777 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5778 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5785 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5797 getAutoCreate : function()
5799 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5805 if (Roo.bootstrap.version == 4) {
5806 if (['tabs','pills'].indexOf(this.type) != -1) {
5807 cfg.cls += ' nav-' + this.type;
5809 // trying to remove so header bar can right align top?
5810 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5811 // do not use on header bar...
5812 cfg.cls += ' navbar-nav';
5817 if (['tabs','pills'].indexOf(this.type) != -1) {
5818 cfg.cls += ' nav-' + this.type
5820 if (this.type !== 'nav') {
5821 Roo.log('nav type must be nav/tabs/pills')
5823 cfg.cls += ' navbar-nav'
5827 if (this.parent() && this.parent().sidebar) {
5830 cls: 'dashboard-menu sidebar-menu'
5836 if (this.form === true) {
5839 cls: 'navbar-form form-inline'
5841 //nav navbar-right ml-md-auto
5842 if (this.align === 'right') {
5843 cfg.cls += ' navbar-right ml-md-auto';
5845 cfg.cls += ' navbar-left';
5849 if (this.align === 'right') {
5850 cfg.cls += ' navbar-right ml-md-auto';
5852 cfg.cls += ' mr-auto';
5856 cfg.cls += ' navbar-inverse';
5864 * sets the active Navigation item
5865 * @param {Roo.bootstrap.NavItem} the new current navitem
5867 setActiveItem : function(item)
5870 Roo.each(this.navItems, function(v){
5875 v.setActive(false, true);
5882 item.setActive(true, true);
5883 this.fireEvent('changed', this, item, prev);
5888 * gets the active Navigation item
5889 * @return {Roo.bootstrap.NavItem} the current navitem
5891 getActive : function()
5895 Roo.each(this.navItems, function(v){
5906 indexOfNav : function()
5910 Roo.each(this.navItems, function(v,i){
5921 * adds a Navigation item
5922 * @param {Roo.bootstrap.NavItem} the navitem to add
5924 addItem : function(cfg)
5926 if (this.form && Roo.bootstrap.version == 4) {
5929 var cn = new Roo.bootstrap.NavItem(cfg);
5931 cn.parentId = this.id;
5932 cn.onRender(this.el, null);
5936 * register a Navigation item
5937 * @param {Roo.bootstrap.NavItem} the navitem to add
5939 register : function(item)
5941 this.navItems.push( item);
5942 item.navId = this.navId;
5947 * clear all the Navigation item
5950 clearAll : function()
5953 this.el.dom.innerHTML = '';
5956 getNavItem: function(tabId)
5959 Roo.each(this.navItems, function(e) {
5960 if (e.tabId == tabId) {
5970 setActiveNext : function()
5972 var i = this.indexOfNav(this.getActive());
5973 if (i > this.navItems.length) {
5976 this.setActiveItem(this.navItems[i+1]);
5978 setActivePrev : function()
5980 var i = this.indexOfNav(this.getActive());
5984 this.setActiveItem(this.navItems[i-1]);
5986 clearWasActive : function(except) {
5987 Roo.each(this.navItems, function(e) {
5988 if (e.tabId != except.tabId && e.was_active) {
5989 e.was_active = false;
5996 getWasActive : function ()
5999 Roo.each(this.navItems, function(e) {
6014 Roo.apply(Roo.bootstrap.NavGroup, {
6018 * register a Navigation Group
6019 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6021 register : function(navgrp)
6023 this.groups[navgrp.navId] = navgrp;
6027 * fetch a Navigation Group based on the navigation ID
6028 * @param {string} the navgroup to add
6029 * @returns {Roo.bootstrap.NavGroup} the navgroup
6031 get: function(navId) {
6032 if (typeof(this.groups[navId]) == 'undefined') {
6034 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6036 return this.groups[navId] ;
6051 * @class Roo.bootstrap.NavItem
6052 * @extends Roo.bootstrap.Component
6053 * Bootstrap Navbar.NavItem class
6054 * @cfg {String} href link to
6055 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6056 * @cfg {Boolean} button_outline show and outlined button
6057 * @cfg {String} html content of button
6058 * @cfg {String} badge text inside badge
6059 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6060 * @cfg {String} glyphicon DEPRICATED - use fa
6061 * @cfg {String} icon DEPRICATED - use fa
6062 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6063 * @cfg {Boolean} active Is item active
6064 * @cfg {Boolean} disabled Is item disabled
6065 * @cfg {String} linkcls Link Class
6066 * @cfg {Boolean} preventDefault (true | false) default false
6067 * @cfg {String} tabId the tab that this item activates.
6068 * @cfg {String} tagtype (a|span) render as a href or span?
6069 * @cfg {Boolean} animateRef (true|false) link to element default false
6072 * Create a new Navbar Item
6073 * @param {Object} config The config object
6075 Roo.bootstrap.NavItem = function(config){
6076 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6081 * The raw click event for the entire grid.
6082 * @param {Roo.EventObject} e
6087 * Fires when the active item active state changes
6088 * @param {Roo.bootstrap.NavItem} this
6089 * @param {boolean} state the new state
6095 * Fires when scroll to element
6096 * @param {Roo.bootstrap.NavItem} this
6097 * @param {Object} options
6098 * @param {Roo.EventObject} e
6106 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6115 preventDefault : false,
6123 button_outline : false,
6127 getAutoCreate : function(){
6134 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6137 cfg.cls += ' active' ;
6139 if (this.disabled) {
6140 cfg.cls += ' disabled';
6144 if (this.button_weight.length) {
6145 cfg.tag = this.href ? 'a' : 'button';
6146 cfg.html = this.html || '';
6147 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6149 cfg.href = this.href;
6152 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6155 // menu .. should add dropdown-menu class - so no need for carat..
6157 if (this.badge !== '') {
6159 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6164 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6168 href : this.href || "#",
6169 html: this.html || ''
6172 if (this.tagtype == 'a') {
6173 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6177 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6180 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6182 if(this.glyphicon) {
6183 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6188 cfg.cn[0].html += " <span class='caret'></span>";
6192 if (this.badge !== '') {
6194 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6202 onRender : function(ct, position)
6204 // Roo.log("Call onRender: " + this.xtype);
6205 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6209 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6210 this.navLink = this.el.select('.nav-link',true).first();
6215 initEvents: function()
6217 if (typeof (this.menu) != 'undefined') {
6218 this.menu.parentType = this.xtype;
6219 this.menu.triggerEl = this.el;
6220 this.menu = this.addxtype(Roo.apply({}, this.menu));
6223 this.el.on('click', this.onClick, this);
6225 //if(this.tagtype == 'span'){
6226 // this.el.select('span',true).on('click', this.onClick, this);
6229 // at this point parent should be available..
6230 this.parent().register(this);
6233 onClick : function(e)
6235 if (e.getTarget('.dropdown-menu-item')) {
6236 // did you click on a menu itemm.... - then don't trigger onclick..
6241 this.preventDefault ||
6244 Roo.log("NavItem - prevent Default?");
6248 if (this.disabled) {
6252 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6253 if (tg && tg.transition) {
6254 Roo.log("waiting for the transitionend");
6260 //Roo.log("fire event clicked");
6261 if(this.fireEvent('click', this, e) === false){
6265 if(this.tagtype == 'span'){
6269 //Roo.log(this.href);
6270 var ael = this.el.select('a',true).first();
6273 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6274 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6275 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6276 return; // ignore... - it's a 'hash' to another page.
6278 Roo.log("NavItem - prevent Default?");
6280 this.scrollToElement(e);
6284 var p = this.parent();
6286 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6287 if (typeof(p.setActiveItem) !== 'undefined') {
6288 p.setActiveItem(this);
6292 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6293 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6294 // remove the collapsed menu expand...
6295 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6299 isActive: function () {
6302 setActive : function(state, fire, is_was_active)
6304 if (this.active && !state && this.navId) {
6305 this.was_active = true;
6306 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6308 nv.clearWasActive(this);
6312 this.active = state;
6315 this.el.removeClass('active');
6316 this.navLink ? this.navLink.removeClass('active') : false;
6317 } else if (!this.el.hasClass('active')) {
6319 this.el.addClass('active');
6320 if (Roo.bootstrap.version == 4 && this.navLink ) {
6321 this.navLink.addClass('active');
6326 this.fireEvent('changed', this, state);
6329 // show a panel if it's registered and related..
6331 if (!this.navId || !this.tabId || !state || is_was_active) {
6335 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6339 var pan = tg.getPanelByName(this.tabId);
6343 // if we can not flip to new panel - go back to old nav highlight..
6344 if (false == tg.showPanel(pan)) {
6345 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6347 var onav = nv.getWasActive();
6349 onav.setActive(true, false, true);
6358 // this should not be here...
6359 setDisabled : function(state)
6361 this.disabled = state;
6363 this.el.removeClass('disabled');
6364 } else if (!this.el.hasClass('disabled')) {
6365 this.el.addClass('disabled');
6371 * Fetch the element to display the tooltip on.
6372 * @return {Roo.Element} defaults to this.el
6374 tooltipEl : function()
6376 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6379 scrollToElement : function(e)
6381 var c = document.body;
6384 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6386 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6387 c = document.documentElement;
6390 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6396 var o = target.calcOffsetsTo(c);
6403 this.fireEvent('scrollto', this, options, e);
6405 Roo.get(c).scrollTo('top', options.value, true);
6418 * <span> icon </span>
6419 * <span> text </span>
6420 * <span>badge </span>
6424 * @class Roo.bootstrap.NavSidebarItem
6425 * @extends Roo.bootstrap.NavItem
6426 * Bootstrap Navbar.NavSidebarItem class
6427 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6428 * {Boolean} open is the menu open
6429 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6430 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6431 * {String} buttonSize (sm|md|lg)the extra classes for the button
6432 * {Boolean} showArrow show arrow next to the text (default true)
6434 * Create a new Navbar Button
6435 * @param {Object} config The config object
6437 Roo.bootstrap.NavSidebarItem = function(config){
6438 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6443 * The raw click event for the entire grid.
6444 * @param {Roo.EventObject} e
6449 * Fires when the active item active state changes
6450 * @param {Roo.bootstrap.NavSidebarItem} this
6451 * @param {boolean} state the new state
6459 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6461 badgeWeight : 'default',
6467 buttonWeight : 'default',
6473 getAutoCreate : function(){
6478 href : this.href || '#',
6484 if(this.buttonView){
6487 href : this.href || '#',
6488 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6501 cfg.cls += ' active';
6504 if (this.disabled) {
6505 cfg.cls += ' disabled';
6508 cfg.cls += ' open x-open';
6511 if (this.glyphicon || this.icon) {
6512 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6513 a.cn.push({ tag : 'i', cls : c }) ;
6516 if(!this.buttonView){
6519 html : this.html || ''
6526 if (this.badge !== '') {
6527 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6533 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6536 a.cls += ' dropdown-toggle treeview' ;
6542 initEvents : function()
6544 if (typeof (this.menu) != 'undefined') {
6545 this.menu.parentType = this.xtype;
6546 this.menu.triggerEl = this.el;
6547 this.menu = this.addxtype(Roo.apply({}, this.menu));
6550 this.el.on('click', this.onClick, this);
6552 if(this.badge !== ''){
6553 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6558 onClick : function(e)
6565 if(this.preventDefault){
6569 this.fireEvent('click', this, e);
6572 disable : function()
6574 this.setDisabled(true);
6579 this.setDisabled(false);
6582 setDisabled : function(state)
6584 if(this.disabled == state){
6588 this.disabled = state;
6591 this.el.addClass('disabled');
6595 this.el.removeClass('disabled');
6600 setActive : function(state)
6602 if(this.active == state){
6606 this.active = state;
6609 this.el.addClass('active');
6613 this.el.removeClass('active');
6618 isActive: function ()
6623 setBadge : function(str)
6629 this.badgeEl.dom.innerHTML = str;
6644 Roo.namespace('Roo.bootstrap.breadcrumb');
6648 * @class Roo.bootstrap.breadcrumb.Nav
6649 * @extends Roo.bootstrap.Component
6650 * Bootstrap Breadcrumb Nav Class
6652 * @children Roo.bootstrap.breadcrumb.Item
6655 * Create a new breadcrumb.Nav
6656 * @param {Object} config The config object
6660 Roo.bootstrap.breadcrumb.Nav = function(config){
6661 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6666 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6668 getAutoCreate : function()
6685 initEvents: function()
6687 this.olEl = this.el.select('ol',true).first();
6689 getChildContainer : function()
6705 * @class Roo.bootstrap.breadcrumb.Nav
6706 * @extends Roo.bootstrap.Component
6707 * Bootstrap Breadcrumb Nav Class
6709 * @children Roo.bootstrap.breadcrumb.Component
6710 * @cfg {String} html the content of the link.
6711 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6712 * @cfg {Boolean} active is it active
6716 * Create a new breadcrumb.Nav
6717 * @param {Object} config The config object
6720 Roo.bootstrap.breadcrumb.Item = function(config){
6721 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6726 * The img click event for the img.
6727 * @param {Roo.EventObject} e
6734 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6739 getAutoCreate : function()
6744 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6746 if (this.href !== false) {
6753 cfg.html = this.html;
6759 initEvents: function()
6762 this.el.select('a', true).first().on('click',this.onClick, this)
6766 onClick : function(e)
6769 this.fireEvent('click',this, e);
6782 * @class Roo.bootstrap.Row
6783 * @extends Roo.bootstrap.Component
6784 * Bootstrap Row class (contains columns...)
6788 * @param {Object} config The config object
6791 Roo.bootstrap.Row = function(config){
6792 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6795 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6797 getAutoCreate : function(){
6816 * @class Roo.bootstrap.Pagination
6817 * @extends Roo.bootstrap.Component
6818 * Bootstrap Pagination class
6819 * @cfg {String} size xs | sm | md | lg
6820 * @cfg {Boolean} inverse false | true
6823 * Create a new Pagination
6824 * @param {Object} config The config object
6827 Roo.bootstrap.Pagination = function(config){
6828 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6831 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6837 getAutoCreate : function(){
6843 cfg.cls += ' inverse';
6849 cfg.cls += " " + this.cls;
6867 * @class Roo.bootstrap.PaginationItem
6868 * @extends Roo.bootstrap.Component
6869 * Bootstrap PaginationItem class
6870 * @cfg {String} html text
6871 * @cfg {String} href the link
6872 * @cfg {Boolean} preventDefault (true | false) default true
6873 * @cfg {Boolean} active (true | false) default false
6874 * @cfg {Boolean} disabled default false
6878 * Create a new PaginationItem
6879 * @param {Object} config The config object
6883 Roo.bootstrap.PaginationItem = function(config){
6884 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6889 * The raw click event for the entire grid.
6890 * @param {Roo.EventObject} e
6896 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6900 preventDefault: true,
6905 getAutoCreate : function(){
6911 href : this.href ? this.href : '#',
6912 html : this.html ? this.html : ''
6922 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6926 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6932 initEvents: function() {
6934 this.el.on('click', this.onClick, this);
6937 onClick : function(e)
6939 Roo.log('PaginationItem on click ');
6940 if(this.preventDefault){
6948 this.fireEvent('click', this, e);
6964 * @class Roo.bootstrap.Slider
6965 * @extends Roo.bootstrap.Component
6966 * Bootstrap Slider class
6969 * Create a new Slider
6970 * @param {Object} config The config object
6973 Roo.bootstrap.Slider = function(config){
6974 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6977 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6979 getAutoCreate : function(){
6983 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6987 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6999 * Ext JS Library 1.1.1
7000 * Copyright(c) 2006-2007, Ext JS, LLC.
7002 * Originally Released Under LGPL - original licence link has changed is not relivant.
7005 * <script type="text/javascript">
7010 * @class Roo.grid.ColumnModel
7011 * @extends Roo.util.Observable
7012 * This is the default implementation of a ColumnModel used by the Grid. It defines
7013 * the columns in the grid.
7016 var colModel = new Roo.grid.ColumnModel([
7017 {header: "Ticker", width: 60, sortable: true, locked: true},
7018 {header: "Company Name", width: 150, sortable: true},
7019 {header: "Market Cap.", width: 100, sortable: true},
7020 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7021 {header: "Employees", width: 100, sortable: true, resizable: false}
7026 * The config options listed for this class are options which may appear in each
7027 * individual column definition.
7028 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7030 * @param {Object} config An Array of column config objects. See this class's
7031 * config objects for details.
7033 Roo.grid.ColumnModel = function(config){
7035 * The config passed into the constructor
7037 this.config = config;
7040 // if no id, create one
7041 // if the column does not have a dataIndex mapping,
7042 // map it to the order it is in the config
7043 for(var i = 0, len = config.length; i < len; i++){
7045 if(typeof c.dataIndex == "undefined"){
7048 if(typeof c.renderer == "string"){
7049 c.renderer = Roo.util.Format[c.renderer];
7051 if(typeof c.id == "undefined"){
7054 if(c.editor && c.editor.xtype){
7055 c.editor = Roo.factory(c.editor, Roo.grid);
7057 if(c.editor && c.editor.isFormField){
7058 c.editor = new Roo.grid.GridEditor(c.editor);
7060 this.lookup[c.id] = c;
7064 * The width of columns which have no width specified (defaults to 100)
7067 this.defaultWidth = 100;
7070 * Default sortable of columns which have no sortable specified (defaults to false)
7073 this.defaultSortable = false;
7077 * @event widthchange
7078 * Fires when the width of a column changes.
7079 * @param {ColumnModel} this
7080 * @param {Number} columnIndex The column index
7081 * @param {Number} newWidth The new width
7083 "widthchange": true,
7085 * @event headerchange
7086 * Fires when the text of a header changes.
7087 * @param {ColumnModel} this
7088 * @param {Number} columnIndex The column index
7089 * @param {Number} newText The new header text
7091 "headerchange": true,
7093 * @event hiddenchange
7094 * Fires when a column is hidden or "unhidden".
7095 * @param {ColumnModel} this
7096 * @param {Number} columnIndex The column index
7097 * @param {Boolean} hidden true if hidden, false otherwise
7099 "hiddenchange": true,
7101 * @event columnmoved
7102 * Fires when a column is moved.
7103 * @param {ColumnModel} this
7104 * @param {Number} oldIndex
7105 * @param {Number} newIndex
7107 "columnmoved" : true,
7109 * @event columlockchange
7110 * Fires when a column's locked state is changed
7111 * @param {ColumnModel} this
7112 * @param {Number} colIndex
7113 * @param {Boolean} locked true if locked
7115 "columnlockchange" : true
7117 Roo.grid.ColumnModel.superclass.constructor.call(this);
7119 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7121 * @cfg {String} header The header text to display in the Grid view.
7124 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7125 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7126 * specified, the column's index is used as an index into the Record's data Array.
7129 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7130 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7133 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7134 * Defaults to the value of the {@link #defaultSortable} property.
7135 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7138 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7141 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7144 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7147 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7150 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7151 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7152 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7153 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7156 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7159 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7162 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7165 * @cfg {String} cursor (Optional)
7168 * @cfg {String} tooltip (Optional)
7171 * @cfg {Number} xs (Optional)
7174 * @cfg {Number} sm (Optional)
7177 * @cfg {Number} md (Optional)
7180 * @cfg {Number} lg (Optional)
7183 * Returns the id of the column at the specified index.
7184 * @param {Number} index The column index
7185 * @return {String} the id
7187 getColumnId : function(index){
7188 return this.config[index].id;
7192 * Returns the column for a specified id.
7193 * @param {String} id The column id
7194 * @return {Object} the column
7196 getColumnById : function(id){
7197 return this.lookup[id];
7202 * Returns the column for a specified dataIndex.
7203 * @param {String} dataIndex The column dataIndex
7204 * @return {Object|Boolean} the column or false if not found
7206 getColumnByDataIndex: function(dataIndex){
7207 var index = this.findColumnIndex(dataIndex);
7208 return index > -1 ? this.config[index] : false;
7212 * Returns the index for a specified column id.
7213 * @param {String} id The column id
7214 * @return {Number} the index, or -1 if not found
7216 getIndexById : function(id){
7217 for(var i = 0, len = this.config.length; i < len; i++){
7218 if(this.config[i].id == id){
7226 * Returns the index for a specified column dataIndex.
7227 * @param {String} dataIndex The column dataIndex
7228 * @return {Number} the index, or -1 if not found
7231 findColumnIndex : function(dataIndex){
7232 for(var i = 0, len = this.config.length; i < len; i++){
7233 if(this.config[i].dataIndex == dataIndex){
7241 moveColumn : function(oldIndex, newIndex){
7242 var c = this.config[oldIndex];
7243 this.config.splice(oldIndex, 1);
7244 this.config.splice(newIndex, 0, c);
7245 this.dataMap = null;
7246 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7249 isLocked : function(colIndex){
7250 return this.config[colIndex].locked === true;
7253 setLocked : function(colIndex, value, suppressEvent){
7254 if(this.isLocked(colIndex) == value){
7257 this.config[colIndex].locked = value;
7259 this.fireEvent("columnlockchange", this, colIndex, value);
7263 getTotalLockedWidth : function(){
7265 for(var i = 0; i < this.config.length; i++){
7266 if(this.isLocked(i) && !this.isHidden(i)){
7267 this.totalWidth += this.getColumnWidth(i);
7273 getLockedCount : function(){
7274 for(var i = 0, len = this.config.length; i < len; i++){
7275 if(!this.isLocked(i)){
7280 return this.config.length;
7284 * Returns the number of columns.
7287 getColumnCount : function(visibleOnly){
7288 if(visibleOnly === true){
7290 for(var i = 0, len = this.config.length; i < len; i++){
7291 if(!this.isHidden(i)){
7297 return this.config.length;
7301 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7302 * @param {Function} fn
7303 * @param {Object} scope (optional)
7304 * @return {Array} result
7306 getColumnsBy : function(fn, scope){
7308 for(var i = 0, len = this.config.length; i < len; i++){
7309 var c = this.config[i];
7310 if(fn.call(scope||this, c, i) === true){
7318 * Returns true if the specified column is sortable.
7319 * @param {Number} col The column index
7322 isSortable : function(col){
7323 if(typeof this.config[col].sortable == "undefined"){
7324 return this.defaultSortable;
7326 return this.config[col].sortable;
7330 * Returns the rendering (formatting) function defined for the column.
7331 * @param {Number} col The column index.
7332 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7334 getRenderer : function(col){
7335 if(!this.config[col].renderer){
7336 return Roo.grid.ColumnModel.defaultRenderer;
7338 return this.config[col].renderer;
7342 * Sets the rendering (formatting) function for a column.
7343 * @param {Number} col The column index
7344 * @param {Function} fn The function to use to process the cell's raw data
7345 * to return HTML markup for the grid view. The render function is called with
7346 * the following parameters:<ul>
7347 * <li>Data value.</li>
7348 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7349 * <li>css A CSS style string to apply to the table cell.</li>
7350 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7351 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7352 * <li>Row index</li>
7353 * <li>Column index</li>
7354 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7356 setRenderer : function(col, fn){
7357 this.config[col].renderer = fn;
7361 * Returns the width for the specified column.
7362 * @param {Number} col The column index
7365 getColumnWidth : function(col){
7366 return this.config[col].width * 1 || this.defaultWidth;
7370 * Sets the width for a column.
7371 * @param {Number} col The column index
7372 * @param {Number} width The new width
7374 setColumnWidth : function(col, width, suppressEvent){
7375 this.config[col].width = width;
7376 this.totalWidth = null;
7378 this.fireEvent("widthchange", this, col, width);
7383 * Returns the total width of all columns.
7384 * @param {Boolean} includeHidden True to include hidden column widths
7387 getTotalWidth : function(includeHidden){
7388 if(!this.totalWidth){
7389 this.totalWidth = 0;
7390 for(var i = 0, len = this.config.length; i < len; i++){
7391 if(includeHidden || !this.isHidden(i)){
7392 this.totalWidth += this.getColumnWidth(i);
7396 return this.totalWidth;
7400 * Returns the header for the specified column.
7401 * @param {Number} col The column index
7404 getColumnHeader : function(col){
7405 return this.config[col].header;
7409 * Sets the header for a column.
7410 * @param {Number} col The column index
7411 * @param {String} header The new header
7413 setColumnHeader : function(col, header){
7414 this.config[col].header = header;
7415 this.fireEvent("headerchange", this, col, header);
7419 * Returns the tooltip for the specified column.
7420 * @param {Number} col The column index
7423 getColumnTooltip : function(col){
7424 return this.config[col].tooltip;
7427 * Sets the tooltip for a column.
7428 * @param {Number} col The column index
7429 * @param {String} tooltip The new tooltip
7431 setColumnTooltip : function(col, tooltip){
7432 this.config[col].tooltip = tooltip;
7436 * Returns the dataIndex for the specified column.
7437 * @param {Number} col The column index
7440 getDataIndex : function(col){
7441 return this.config[col].dataIndex;
7445 * Sets the dataIndex for a column.
7446 * @param {Number} col The column index
7447 * @param {Number} dataIndex The new dataIndex
7449 setDataIndex : function(col, dataIndex){
7450 this.config[col].dataIndex = dataIndex;
7456 * Returns true if the cell is editable.
7457 * @param {Number} colIndex The column index
7458 * @param {Number} rowIndex The row index - this is nto actually used..?
7461 isCellEditable : function(colIndex, rowIndex){
7462 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7466 * Returns the editor defined for the cell/column.
7467 * return false or null to disable editing.
7468 * @param {Number} colIndex The column index
7469 * @param {Number} rowIndex The row index
7472 getCellEditor : function(colIndex, rowIndex){
7473 return this.config[colIndex].editor;
7477 * Sets if a column is editable.
7478 * @param {Number} col The column index
7479 * @param {Boolean} editable True if the column is editable
7481 setEditable : function(col, editable){
7482 this.config[col].editable = editable;
7487 * Returns true if the column is hidden.
7488 * @param {Number} colIndex The column index
7491 isHidden : function(colIndex){
7492 return this.config[colIndex].hidden;
7497 * Returns true if the column width cannot be changed
7499 isFixed : function(colIndex){
7500 return this.config[colIndex].fixed;
7504 * Returns true if the column can be resized
7507 isResizable : function(colIndex){
7508 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7511 * Sets if a column is hidden.
7512 * @param {Number} colIndex The column index
7513 * @param {Boolean} hidden True if the column is hidden
7515 setHidden : function(colIndex, hidden){
7516 this.config[colIndex].hidden = hidden;
7517 this.totalWidth = null;
7518 this.fireEvent("hiddenchange", this, colIndex, hidden);
7522 * Sets the editor for a column.
7523 * @param {Number} col The column index
7524 * @param {Object} editor The editor object
7526 setEditor : function(col, editor){
7527 this.config[col].editor = editor;
7531 Roo.grid.ColumnModel.defaultRenderer = function(value)
7533 if(typeof value == "object") {
7536 if(typeof value == "string" && value.length < 1){
7540 return String.format("{0}", value);
7543 // Alias for backwards compatibility
7544 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7547 * Ext JS Library 1.1.1
7548 * Copyright(c) 2006-2007, Ext JS, LLC.
7550 * Originally Released Under LGPL - original licence link has changed is not relivant.
7553 * <script type="text/javascript">
7557 * @class Roo.LoadMask
7558 * A simple utility class for generically masking elements while loading data. If the element being masked has
7559 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7560 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7561 * element's UpdateManager load indicator and will be destroyed after the initial load.
7563 * Create a new LoadMask
7564 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7565 * @param {Object} config The config object
7567 Roo.LoadMask = function(el, config){
7568 this.el = Roo.get(el);
7569 Roo.apply(this, config);
7571 this.store.on('beforeload', this.onBeforeLoad, this);
7572 this.store.on('load', this.onLoad, this);
7573 this.store.on('loadexception', this.onLoadException, this);
7574 this.removeMask = false;
7576 var um = this.el.getUpdateManager();
7577 um.showLoadIndicator = false; // disable the default indicator
7578 um.on('beforeupdate', this.onBeforeLoad, this);
7579 um.on('update', this.onLoad, this);
7580 um.on('failure', this.onLoad, this);
7581 this.removeMask = true;
7585 Roo.LoadMask.prototype = {
7587 * @cfg {Boolean} removeMask
7588 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7589 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7593 * The text to display in a centered loading message box (defaults to 'Loading...')
7597 * @cfg {String} msgCls
7598 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7600 msgCls : 'x-mask-loading',
7603 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7609 * Disables the mask to prevent it from being displayed
7611 disable : function(){
7612 this.disabled = true;
7616 * Enables the mask so that it can be displayed
7618 enable : function(){
7619 this.disabled = false;
7622 onLoadException : function()
7626 if (typeof(arguments[3]) != 'undefined') {
7627 Roo.MessageBox.alert("Error loading",arguments[3]);
7631 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7632 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7639 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7644 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7648 onBeforeLoad : function(){
7650 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7655 destroy : function(){
7657 this.store.un('beforeload', this.onBeforeLoad, this);
7658 this.store.un('load', this.onLoad, this);
7659 this.store.un('loadexception', this.onLoadException, this);
7661 var um = this.el.getUpdateManager();
7662 um.un('beforeupdate', this.onBeforeLoad, this);
7663 um.un('update', this.onLoad, this);
7664 um.un('failure', this.onLoad, this);
7675 * @class Roo.bootstrap.Table
7676 * @extends Roo.bootstrap.Component
7677 * Bootstrap Table class
7678 * @cfg {String} cls table class
7679 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7680 * @cfg {String} bgcolor Specifies the background color for a table
7681 * @cfg {Number} border Specifies whether the table cells should have borders or not
7682 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7683 * @cfg {Number} cellspacing Specifies the space between cells
7684 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7685 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7686 * @cfg {String} sortable Specifies that the table should be sortable
7687 * @cfg {String} summary Specifies a summary of the content of a table
7688 * @cfg {Number} width Specifies the width of a table
7689 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7691 * @cfg {boolean} striped Should the rows be alternative striped
7692 * @cfg {boolean} bordered Add borders to the table
7693 * @cfg {boolean} hover Add hover highlighting
7694 * @cfg {boolean} condensed Format condensed
7695 * @cfg {boolean} responsive Format condensed
7696 * @cfg {Boolean} loadMask (true|false) default false
7697 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7698 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7699 * @cfg {Boolean} rowSelection (true|false) default false
7700 * @cfg {Boolean} cellSelection (true|false) default false
7701 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7702 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7703 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7704 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7708 * Create a new Table
7709 * @param {Object} config The config object
7712 Roo.bootstrap.Table = function(config){
7713 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7718 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7719 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7720 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7721 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7723 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7725 this.sm.grid = this;
7726 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7727 this.sm = this.selModel;
7728 this.sm.xmodule = this.xmodule || false;
7731 if (this.cm && typeof(this.cm.config) == 'undefined') {
7732 this.colModel = new Roo.grid.ColumnModel(this.cm);
7733 this.cm = this.colModel;
7734 this.cm.xmodule = this.xmodule || false;
7737 this.store= Roo.factory(this.store, Roo.data);
7738 this.ds = this.store;
7739 this.ds.xmodule = this.xmodule || false;
7742 if (this.footer && this.store) {
7743 this.footer.dataSource = this.ds;
7744 this.footer = Roo.factory(this.footer);
7751 * Fires when a cell is clicked
7752 * @param {Roo.bootstrap.Table} this
7753 * @param {Roo.Element} el
7754 * @param {Number} rowIndex
7755 * @param {Number} columnIndex
7756 * @param {Roo.EventObject} e
7760 * @event celldblclick
7761 * Fires when a cell is double clicked
7762 * @param {Roo.bootstrap.Table} this
7763 * @param {Roo.Element} el
7764 * @param {Number} rowIndex
7765 * @param {Number} columnIndex
7766 * @param {Roo.EventObject} e
7768 "celldblclick" : true,
7771 * Fires when a row is clicked
7772 * @param {Roo.bootstrap.Table} this
7773 * @param {Roo.Element} el
7774 * @param {Number} rowIndex
7775 * @param {Roo.EventObject} e
7779 * @event rowdblclick
7780 * Fires when a row is double clicked
7781 * @param {Roo.bootstrap.Table} this
7782 * @param {Roo.Element} el
7783 * @param {Number} rowIndex
7784 * @param {Roo.EventObject} e
7786 "rowdblclick" : true,
7789 * Fires when a mouseover occur
7790 * @param {Roo.bootstrap.Table} this
7791 * @param {Roo.Element} el
7792 * @param {Number} rowIndex
7793 * @param {Number} columnIndex
7794 * @param {Roo.EventObject} e
7799 * Fires when a mouseout occur
7800 * @param {Roo.bootstrap.Table} this
7801 * @param {Roo.Element} el
7802 * @param {Number} rowIndex
7803 * @param {Number} columnIndex
7804 * @param {Roo.EventObject} e
7809 * Fires when a row is rendered, so you can change add a style to it.
7810 * @param {Roo.bootstrap.Table} this
7811 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7815 * @event rowsrendered
7816 * Fires when all the rows have been rendered
7817 * @param {Roo.bootstrap.Table} this
7819 'rowsrendered' : true,
7821 * @event contextmenu
7822 * The raw contextmenu event for the entire grid.
7823 * @param {Roo.EventObject} e
7825 "contextmenu" : true,
7827 * @event rowcontextmenu
7828 * Fires when a row is right clicked
7829 * @param {Roo.bootstrap.Table} this
7830 * @param {Number} rowIndex
7831 * @param {Roo.EventObject} e
7833 "rowcontextmenu" : true,
7835 * @event cellcontextmenu
7836 * Fires when a cell is right clicked
7837 * @param {Roo.bootstrap.Table} this
7838 * @param {Number} rowIndex
7839 * @param {Number} cellIndex
7840 * @param {Roo.EventObject} e
7842 "cellcontextmenu" : true,
7844 * @event headercontextmenu
7845 * Fires when a header is right clicked
7846 * @param {Roo.bootstrap.Table} this
7847 * @param {Number} columnIndex
7848 * @param {Roo.EventObject} e
7850 "headercontextmenu" : true
7854 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7880 rowSelection : false,
7881 cellSelection : false,
7884 // Roo.Element - the tbody
7886 // Roo.Element - thead element
7889 container: false, // used by gridpanel...
7895 auto_hide_footer : false,
7897 getAutoCreate : function()
7899 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7906 if (this.scrollBody) {
7907 cfg.cls += ' table-body-fixed';
7910 cfg.cls += ' table-striped';
7914 cfg.cls += ' table-hover';
7916 if (this.bordered) {
7917 cfg.cls += ' table-bordered';
7919 if (this.condensed) {
7920 cfg.cls += ' table-condensed';
7922 if (this.responsive) {
7923 cfg.cls += ' table-responsive';
7927 cfg.cls+= ' ' +this.cls;
7930 // this lot should be simplifed...
7943 ].forEach(function(k) {
7951 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7954 if(this.store || this.cm){
7955 if(this.headerShow){
7956 cfg.cn.push(this.renderHeader());
7959 cfg.cn.push(this.renderBody());
7961 if(this.footerShow){
7962 cfg.cn.push(this.renderFooter());
7964 // where does this come from?
7965 //cfg.cls+= ' TableGrid';
7968 return { cn : [ cfg ] };
7971 initEvents : function()
7973 if(!this.store || !this.cm){
7976 if (this.selModel) {
7977 this.selModel.initEvents();
7981 //Roo.log('initEvents with ds!!!!');
7983 this.mainBody = this.el.select('tbody', true).first();
7984 this.mainHead = this.el.select('thead', true).first();
7985 this.mainFoot = this.el.select('tfoot', true).first();
7991 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7992 e.on('click', _this.sort, _this);
7995 this.mainBody.on("click", this.onClick, this);
7996 this.mainBody.on("dblclick", this.onDblClick, this);
7998 // why is this done????? = it breaks dialogs??
7999 //this.parent().el.setStyle('position', 'relative');
8003 this.footer.parentId = this.id;
8004 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8007 this.el.select('tfoot tr td').first().addClass('hide');
8012 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8015 this.store.on('load', this.onLoad, this);
8016 this.store.on('beforeload', this.onBeforeLoad, this);
8017 this.store.on('update', this.onUpdate, this);
8018 this.store.on('add', this.onAdd, this);
8019 this.store.on("clear", this.clear, this);
8021 this.el.on("contextmenu", this.onContextMenu, this);
8023 this.mainBody.on('scroll', this.onBodyScroll, this);
8025 this.cm.on("headerchange", this.onHeaderChange, this);
8027 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8031 onContextMenu : function(e, t)
8033 this.processEvent("contextmenu", e);
8036 processEvent : function(name, e)
8038 if (name != 'touchstart' ) {
8039 this.fireEvent(name, e);
8042 var t = e.getTarget();
8044 var cell = Roo.get(t);
8050 if(cell.findParent('tfoot', false, true)){
8054 if(cell.findParent('thead', false, true)){
8056 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8057 cell = Roo.get(t).findParent('th', false, true);
8059 Roo.log("failed to find th in thead?");
8060 Roo.log(e.getTarget());
8065 var cellIndex = cell.dom.cellIndex;
8067 var ename = name == 'touchstart' ? 'click' : name;
8068 this.fireEvent("header" + ename, this, cellIndex, e);
8073 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8074 cell = Roo.get(t).findParent('td', false, true);
8076 Roo.log("failed to find th in tbody?");
8077 Roo.log(e.getTarget());
8082 var row = cell.findParent('tr', false, true);
8083 var cellIndex = cell.dom.cellIndex;
8084 var rowIndex = row.dom.rowIndex - 1;
8088 this.fireEvent("row" + name, this, rowIndex, e);
8092 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8098 onMouseover : function(e, el)
8100 var cell = Roo.get(el);
8106 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8107 cell = cell.findParent('td', false, true);
8110 var row = cell.findParent('tr', false, true);
8111 var cellIndex = cell.dom.cellIndex;
8112 var rowIndex = row.dom.rowIndex - 1; // start from 0
8114 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8118 onMouseout : function(e, el)
8120 var cell = Roo.get(el);
8126 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8127 cell = cell.findParent('td', false, true);
8130 var row = cell.findParent('tr', false, true);
8131 var cellIndex = cell.dom.cellIndex;
8132 var rowIndex = row.dom.rowIndex - 1; // start from 0
8134 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8138 onClick : function(e, el)
8140 var cell = Roo.get(el);
8142 if(!cell || (!this.cellSelection && !this.rowSelection)){
8146 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8147 cell = cell.findParent('td', false, true);
8150 if(!cell || typeof(cell) == 'undefined'){
8154 var row = cell.findParent('tr', false, true);
8156 if(!row || typeof(row) == 'undefined'){
8160 var cellIndex = cell.dom.cellIndex;
8161 var rowIndex = this.getRowIndex(row);
8163 // why??? - should these not be based on SelectionModel?
8164 if(this.cellSelection){
8165 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8168 if(this.rowSelection){
8169 this.fireEvent('rowclick', this, row, rowIndex, e);
8175 onDblClick : function(e,el)
8177 var cell = Roo.get(el);
8179 if(!cell || (!this.cellSelection && !this.rowSelection)){
8183 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8184 cell = cell.findParent('td', false, true);
8187 if(!cell || typeof(cell) == 'undefined'){
8191 var row = cell.findParent('tr', false, true);
8193 if(!row || typeof(row) == 'undefined'){
8197 var cellIndex = cell.dom.cellIndex;
8198 var rowIndex = this.getRowIndex(row);
8200 if(this.cellSelection){
8201 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8204 if(this.rowSelection){
8205 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8209 sort : function(e,el)
8211 var col = Roo.get(el);
8213 if(!col.hasClass('sortable')){
8217 var sort = col.attr('sort');
8220 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8224 this.store.sortInfo = {field : sort, direction : dir};
8227 Roo.log("calling footer first");
8228 this.footer.onClick('first');
8231 this.store.load({ params : { start : 0 } });
8235 renderHeader : function()
8243 this.totalWidth = 0;
8245 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8247 var config = cm.config[i];
8251 cls : 'x-hcol-' + i,
8253 html: cm.getColumnHeader(i)
8258 if(typeof(config.sortable) != 'undefined' && config.sortable){
8260 c.html = '<i class="glyphicon"></i>' + c.html;
8263 // could use BS4 hidden-..-down
8265 if(typeof(config.lgHeader) != 'undefined'){
8266 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8269 if(typeof(config.mdHeader) != 'undefined'){
8270 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8273 if(typeof(config.smHeader) != 'undefined'){
8274 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8277 if(typeof(config.xsHeader) != 'undefined'){
8278 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8285 if(typeof(config.tooltip) != 'undefined'){
8286 c.tooltip = config.tooltip;
8289 if(typeof(config.colspan) != 'undefined'){
8290 c.colspan = config.colspan;
8293 if(typeof(config.hidden) != 'undefined' && config.hidden){
8294 c.style += ' display:none;';
8297 if(typeof(config.dataIndex) != 'undefined'){
8298 c.sort = config.dataIndex;
8303 if(typeof(config.align) != 'undefined' && config.align.length){
8304 c.style += ' text-align:' + config.align + ';';
8307 if(typeof(config.width) != 'undefined'){
8308 c.style += ' width:' + config.width + 'px;';
8309 this.totalWidth += config.width;
8311 this.totalWidth += 100; // assume minimum of 100 per column?
8314 if(typeof(config.cls) != 'undefined'){
8315 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8318 ['xs','sm','md','lg'].map(function(size){
8320 if(typeof(config[size]) == 'undefined'){
8324 if (!config[size]) { // 0 = hidden
8325 // BS 4 '0' is treated as hide that column and below.
8326 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8330 c.cls += ' col-' + size + '-' + config[size] + (
8331 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8343 renderBody : function()
8353 colspan : this.cm.getColumnCount()
8363 renderFooter : function()
8373 colspan : this.cm.getColumnCount()
8387 // Roo.log('ds onload');
8392 var ds = this.store;
8394 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8395 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8396 if (_this.store.sortInfo) {
8398 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8399 e.select('i', true).addClass(['glyphicon-arrow-up']);
8402 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8403 e.select('i', true).addClass(['glyphicon-arrow-down']);
8408 var tbody = this.mainBody;
8410 if(ds.getCount() > 0){
8411 ds.data.each(function(d,rowIndex){
8412 var row = this.renderRow(cm, ds, rowIndex);
8414 tbody.createChild(row);
8418 if(row.cellObjects.length){
8419 Roo.each(row.cellObjects, function(r){
8420 _this.renderCellObject(r);
8427 var tfoot = this.el.select('tfoot', true).first();
8429 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8431 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8433 var total = this.ds.getTotalCount();
8435 if(this.footer.pageSize < total){
8436 this.mainFoot.show();
8440 Roo.each(this.el.select('tbody td', true).elements, function(e){
8441 e.on('mouseover', _this.onMouseover, _this);
8444 Roo.each(this.el.select('tbody td', true).elements, function(e){
8445 e.on('mouseout', _this.onMouseout, _this);
8447 this.fireEvent('rowsrendered', this);
8453 onUpdate : function(ds,record)
8455 this.refreshRow(record);
8459 onRemove : function(ds, record, index, isUpdate){
8460 if(isUpdate !== true){
8461 this.fireEvent("beforerowremoved", this, index, record);
8463 var bt = this.mainBody.dom;
8465 var rows = this.el.select('tbody > tr', true).elements;
8467 if(typeof(rows[index]) != 'undefined'){
8468 bt.removeChild(rows[index].dom);
8471 // if(bt.rows[index]){
8472 // bt.removeChild(bt.rows[index]);
8475 if(isUpdate !== true){
8476 //this.stripeRows(index);
8477 //this.syncRowHeights(index, index);
8479 this.fireEvent("rowremoved", this, index, record);
8483 onAdd : function(ds, records, rowIndex)
8485 //Roo.log('on Add called');
8486 // - note this does not handle multiple adding very well..
8487 var bt = this.mainBody.dom;
8488 for (var i =0 ; i < records.length;i++) {
8489 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8490 //Roo.log(records[i]);
8491 //Roo.log(this.store.getAt(rowIndex+i));
8492 this.insertRow(this.store, rowIndex + i, false);
8499 refreshRow : function(record){
8500 var ds = this.store, index;
8501 if(typeof record == 'number'){
8503 record = ds.getAt(index);
8505 index = ds.indexOf(record);
8507 return; // should not happen - but seems to
8510 this.insertRow(ds, index, true);
8512 this.onRemove(ds, record, index+1, true);
8514 //this.syncRowHeights(index, index);
8516 this.fireEvent("rowupdated", this, index, record);
8519 insertRow : function(dm, rowIndex, isUpdate){
8522 this.fireEvent("beforerowsinserted", this, rowIndex);
8524 //var s = this.getScrollState();
8525 var row = this.renderRow(this.cm, this.store, rowIndex);
8526 // insert before rowIndex..
8527 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8531 if(row.cellObjects.length){
8532 Roo.each(row.cellObjects, function(r){
8533 _this.renderCellObject(r);
8538 this.fireEvent("rowsinserted", this, rowIndex);
8539 //this.syncRowHeights(firstRow, lastRow);
8540 //this.stripeRows(firstRow);
8547 getRowDom : function(rowIndex)
8549 var rows = this.el.select('tbody > tr', true).elements;
8551 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8554 // returns the object tree for a tr..
8557 renderRow : function(cm, ds, rowIndex)
8559 var d = ds.getAt(rowIndex);
8563 cls : 'x-row-' + rowIndex,
8567 var cellObjects = [];
8569 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8570 var config = cm.config[i];
8572 var renderer = cm.getRenderer(i);
8576 if(typeof(renderer) !== 'undefined'){
8577 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8579 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8580 // and are rendered into the cells after the row is rendered - using the id for the element.
8582 if(typeof(value) === 'object'){
8592 rowIndex : rowIndex,
8597 this.fireEvent('rowclass', this, rowcfg);
8601 cls : rowcfg.rowClass + ' x-col-' + i,
8603 html: (typeof(value) === 'object') ? '' : value
8610 if(typeof(config.colspan) != 'undefined'){
8611 td.colspan = config.colspan;
8614 if(typeof(config.hidden) != 'undefined' && config.hidden){
8615 td.style += ' display:none;';
8618 if(typeof(config.align) != 'undefined' && config.align.length){
8619 td.style += ' text-align:' + config.align + ';';
8621 if(typeof(config.valign) != 'undefined' && config.valign.length){
8622 td.style += ' vertical-align:' + config.valign + ';';
8625 if(typeof(config.width) != 'undefined'){
8626 td.style += ' width:' + config.width + 'px;';
8629 if(typeof(config.cursor) != 'undefined'){
8630 td.style += ' cursor:' + config.cursor + ';';
8633 if(typeof(config.cls) != 'undefined'){
8634 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8637 ['xs','sm','md','lg'].map(function(size){
8639 if(typeof(config[size]) == 'undefined'){
8645 if (!config[size]) { // 0 = hidden
8646 // BS 4 '0' is treated as hide that column and below.
8647 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8651 td.cls += ' col-' + size + '-' + config[size] + (
8652 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8662 row.cellObjects = cellObjects;
8670 onBeforeLoad : function()
8679 this.el.select('tbody', true).first().dom.innerHTML = '';
8682 * Show or hide a row.
8683 * @param {Number} rowIndex to show or hide
8684 * @param {Boolean} state hide
8686 setRowVisibility : function(rowIndex, state)
8688 var bt = this.mainBody.dom;
8690 var rows = this.el.select('tbody > tr', true).elements;
8692 if(typeof(rows[rowIndex]) == 'undefined'){
8695 rows[rowIndex].dom.style.display = state ? '' : 'none';
8699 getSelectionModel : function(){
8701 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8703 return this.selModel;
8706 * Render the Roo.bootstrap object from renderder
8708 renderCellObject : function(r)
8712 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8714 var t = r.cfg.render(r.container);
8717 Roo.each(r.cfg.cn, function(c){
8719 container: t.getChildContainer(),
8722 _this.renderCellObject(child);
8727 getRowIndex : function(row)
8731 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8742 * Returns the grid's underlying element = used by panel.Grid
8743 * @return {Element} The element
8745 getGridEl : function(){
8749 * Forces a resize - used by panel.Grid
8750 * @return {Element} The element
8752 autoSize : function()
8754 //var ctr = Roo.get(this.container.dom.parentElement);
8755 var ctr = Roo.get(this.el.dom);
8757 var thd = this.getGridEl().select('thead',true).first();
8758 var tbd = this.getGridEl().select('tbody', true).first();
8759 var tfd = this.getGridEl().select('tfoot', true).first();
8761 var cw = ctr.getWidth();
8762 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8766 tbd.setWidth(ctr.getWidth());
8767 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8768 // this needs fixing for various usage - currently only hydra job advers I think..
8770 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8772 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8775 cw = Math.max(cw, this.totalWidth);
8776 this.getGridEl().select('tbody tr',true).setWidth(cw);
8778 // resize 'expandable coloumn?
8780 return; // we doe not have a view in this design..
8783 onBodyScroll: function()
8785 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8787 this.mainHead.setStyle({
8788 'position' : 'relative',
8789 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8795 var scrollHeight = this.mainBody.dom.scrollHeight;
8797 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8799 var height = this.mainBody.getHeight();
8801 if(scrollHeight - height == scrollTop) {
8803 var total = this.ds.getTotalCount();
8805 if(this.footer.cursor + this.footer.pageSize < total){
8807 this.footer.ds.load({
8809 start : this.footer.cursor + this.footer.pageSize,
8810 limit : this.footer.pageSize
8820 onHeaderChange : function()
8822 var header = this.renderHeader();
8823 var table = this.el.select('table', true).first();
8825 this.mainHead.remove();
8826 this.mainHead = table.createChild(header, this.mainBody, false);
8829 onHiddenChange : function(colModel, colIndex, hidden)
8831 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8832 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8834 this.CSS.updateRule(thSelector, "display", "");
8835 this.CSS.updateRule(tdSelector, "display", "");
8838 this.CSS.updateRule(thSelector, "display", "none");
8839 this.CSS.updateRule(tdSelector, "display", "none");
8842 this.onHeaderChange();
8846 setColumnWidth: function(col_index, width)
8848 // width = "md-2 xs-2..."
8849 if(!this.colModel.config[col_index]) {
8853 var w = width.split(" ");
8855 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8857 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8860 for(var j = 0; j < w.length; j++) {
8866 var size_cls = w[j].split("-");
8868 if(!Number.isInteger(size_cls[1] * 1)) {
8872 if(!this.colModel.config[col_index][size_cls[0]]) {
8876 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8880 h_row[0].classList.replace(
8881 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8882 "col-"+size_cls[0]+"-"+size_cls[1]
8885 for(var i = 0; i < rows.length; i++) {
8887 var size_cls = w[j].split("-");
8889 if(!Number.isInteger(size_cls[1] * 1)) {
8893 if(!this.colModel.config[col_index][size_cls[0]]) {
8897 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8901 rows[i].classList.replace(
8902 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8903 "col-"+size_cls[0]+"-"+size_cls[1]
8907 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8922 * @class Roo.bootstrap.TableCell
8923 * @extends Roo.bootstrap.Component
8924 * Bootstrap TableCell class
8925 * @cfg {String} html cell contain text
8926 * @cfg {String} cls cell class
8927 * @cfg {String} tag cell tag (td|th) default td
8928 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8929 * @cfg {String} align Aligns the content in a cell
8930 * @cfg {String} axis Categorizes cells
8931 * @cfg {String} bgcolor Specifies the background color of a cell
8932 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8933 * @cfg {Number} colspan Specifies the number of columns a cell should span
8934 * @cfg {String} headers Specifies one or more header cells a cell is related to
8935 * @cfg {Number} height Sets the height of a cell
8936 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8937 * @cfg {Number} rowspan Sets the number of rows a cell should span
8938 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8939 * @cfg {String} valign Vertical aligns the content in a cell
8940 * @cfg {Number} width Specifies the width of a cell
8943 * Create a new TableCell
8944 * @param {Object} config The config object
8947 Roo.bootstrap.TableCell = function(config){
8948 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8951 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8971 getAutoCreate : function(){
8972 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8992 cfg.align=this.align
8998 cfg.bgcolor=this.bgcolor
9001 cfg.charoff=this.charoff
9004 cfg.colspan=this.colspan
9007 cfg.headers=this.headers
9010 cfg.height=this.height
9013 cfg.nowrap=this.nowrap
9016 cfg.rowspan=this.rowspan
9019 cfg.scope=this.scope
9022 cfg.valign=this.valign
9025 cfg.width=this.width
9044 * @class Roo.bootstrap.TableRow
9045 * @extends Roo.bootstrap.Component
9046 * Bootstrap TableRow class
9047 * @cfg {String} cls row class
9048 * @cfg {String} align Aligns the content in a table row
9049 * @cfg {String} bgcolor Specifies a background color for a table row
9050 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9051 * @cfg {String} valign Vertical aligns the content in a table row
9054 * Create a new TableRow
9055 * @param {Object} config The config object
9058 Roo.bootstrap.TableRow = function(config){
9059 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9062 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9070 getAutoCreate : function(){
9071 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9081 cfg.align = this.align;
9084 cfg.bgcolor = this.bgcolor;
9087 cfg.charoff = this.charoff;
9090 cfg.valign = this.valign;
9108 * @class Roo.bootstrap.TableBody
9109 * @extends Roo.bootstrap.Component
9110 * Bootstrap TableBody class
9111 * @cfg {String} cls element class
9112 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9113 * @cfg {String} align Aligns the content inside the element
9114 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9115 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9118 * Create a new TableBody
9119 * @param {Object} config The config object
9122 Roo.bootstrap.TableBody = function(config){
9123 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9126 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9134 getAutoCreate : function(){
9135 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9149 cfg.align = this.align;
9152 cfg.charoff = this.charoff;
9155 cfg.valign = this.valign;
9162 // initEvents : function()
9169 // this.store = Roo.factory(this.store, Roo.data);
9170 // this.store.on('load', this.onLoad, this);
9172 // this.store.load();
9176 // onLoad: function ()
9178 // this.fireEvent('load', this);
9188 * Ext JS Library 1.1.1
9189 * Copyright(c) 2006-2007, Ext JS, LLC.
9191 * Originally Released Under LGPL - original licence link has changed is not relivant.
9194 * <script type="text/javascript">
9197 // as we use this in bootstrap.
9198 Roo.namespace('Roo.form');
9200 * @class Roo.form.Action
9201 * Internal Class used to handle form actions
9203 * @param {Roo.form.BasicForm} el The form element or its id
9204 * @param {Object} config Configuration options
9209 // define the action interface
9210 Roo.form.Action = function(form, options){
9212 this.options = options || {};
9215 * Client Validation Failed
9218 Roo.form.Action.CLIENT_INVALID = 'client';
9220 * Server Validation Failed
9223 Roo.form.Action.SERVER_INVALID = 'server';
9225 * Connect to Server Failed
9228 Roo.form.Action.CONNECT_FAILURE = 'connect';
9230 * Reading Data from Server Failed
9233 Roo.form.Action.LOAD_FAILURE = 'load';
9235 Roo.form.Action.prototype = {
9237 failureType : undefined,
9238 response : undefined,
9242 run : function(options){
9247 success : function(response){
9252 handleResponse : function(response){
9256 // default connection failure
9257 failure : function(response){
9259 this.response = response;
9260 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9261 this.form.afterAction(this, false);
9264 processResponse : function(response){
9265 this.response = response;
9266 if(!response.responseText){
9269 this.result = this.handleResponse(response);
9273 // utility functions used internally
9274 getUrl : function(appendParams){
9275 var url = this.options.url || this.form.url || this.form.el.dom.action;
9277 var p = this.getParams();
9279 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9285 getMethod : function(){
9286 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9289 getParams : function(){
9290 var bp = this.form.baseParams;
9291 var p = this.options.params;
9293 if(typeof p == "object"){
9294 p = Roo.urlEncode(Roo.applyIf(p, bp));
9295 }else if(typeof p == 'string' && bp){
9296 p += '&' + Roo.urlEncode(bp);
9299 p = Roo.urlEncode(bp);
9304 createCallback : function(){
9306 success: this.success,
9307 failure: this.failure,
9309 timeout: (this.form.timeout*1000),
9310 upload: this.form.fileUpload ? this.success : undefined
9315 Roo.form.Action.Submit = function(form, options){
9316 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9319 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9322 haveProgress : false,
9323 uploadComplete : false,
9325 // uploadProgress indicator.
9326 uploadProgress : function()
9328 if (!this.form.progressUrl) {
9332 if (!this.haveProgress) {
9333 Roo.MessageBox.progress("Uploading", "Uploading");
9335 if (this.uploadComplete) {
9336 Roo.MessageBox.hide();
9340 this.haveProgress = true;
9342 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9344 var c = new Roo.data.Connection();
9346 url : this.form.progressUrl,
9351 success : function(req){
9352 //console.log(data);
9356 rdata = Roo.decode(req.responseText)
9358 Roo.log("Invalid data from server..");
9362 if (!rdata || !rdata.success) {
9364 Roo.MessageBox.alert(Roo.encode(rdata));
9367 var data = rdata.data;
9369 if (this.uploadComplete) {
9370 Roo.MessageBox.hide();
9375 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9376 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9379 this.uploadProgress.defer(2000,this);
9382 failure: function(data) {
9383 Roo.log('progress url failed ');
9394 // run get Values on the form, so it syncs any secondary forms.
9395 this.form.getValues();
9397 var o = this.options;
9398 var method = this.getMethod();
9399 var isPost = method == 'POST';
9400 if(o.clientValidation === false || this.form.isValid()){
9402 if (this.form.progressUrl) {
9403 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9404 (new Date() * 1) + '' + Math.random());
9409 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9410 form:this.form.el.dom,
9411 url:this.getUrl(!isPost),
9413 params:isPost ? this.getParams() : null,
9414 isUpload: this.form.fileUpload,
9415 formData : this.form.formData
9418 this.uploadProgress();
9420 }else if (o.clientValidation !== false){ // client validation failed
9421 this.failureType = Roo.form.Action.CLIENT_INVALID;
9422 this.form.afterAction(this, false);
9426 success : function(response)
9428 this.uploadComplete= true;
9429 if (this.haveProgress) {
9430 Roo.MessageBox.hide();
9434 var result = this.processResponse(response);
9435 if(result === true || result.success){
9436 this.form.afterAction(this, true);
9440 this.form.markInvalid(result.errors);
9441 this.failureType = Roo.form.Action.SERVER_INVALID;
9443 this.form.afterAction(this, false);
9445 failure : function(response)
9447 this.uploadComplete= true;
9448 if (this.haveProgress) {
9449 Roo.MessageBox.hide();
9452 this.response = response;
9453 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9454 this.form.afterAction(this, false);
9457 handleResponse : function(response){
9458 if(this.form.errorReader){
9459 var rs = this.form.errorReader.read(response);
9462 for(var i = 0, len = rs.records.length; i < len; i++) {
9463 var r = rs.records[i];
9467 if(errors.length < 1){
9471 success : rs.success,
9477 ret = Roo.decode(response.responseText);
9481 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9491 Roo.form.Action.Load = function(form, options){
9492 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9493 this.reader = this.form.reader;
9496 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9501 Roo.Ajax.request(Roo.apply(
9502 this.createCallback(), {
9503 method:this.getMethod(),
9504 url:this.getUrl(false),
9505 params:this.getParams()
9509 success : function(response){
9511 var result = this.processResponse(response);
9512 if(result === true || !result.success || !result.data){
9513 this.failureType = Roo.form.Action.LOAD_FAILURE;
9514 this.form.afterAction(this, false);
9517 this.form.clearInvalid();
9518 this.form.setValues(result.data);
9519 this.form.afterAction(this, true);
9522 handleResponse : function(response){
9523 if(this.form.reader){
9524 var rs = this.form.reader.read(response);
9525 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9527 success : rs.success,
9531 return Roo.decode(response.responseText);
9535 Roo.form.Action.ACTION_TYPES = {
9536 'load' : Roo.form.Action.Load,
9537 'submit' : Roo.form.Action.Submit
9546 * @class Roo.bootstrap.Form
9547 * @extends Roo.bootstrap.Component
9548 * Bootstrap Form class
9549 * @cfg {String} method GET | POST (default POST)
9550 * @cfg {String} labelAlign top | left (default top)
9551 * @cfg {String} align left | right - for navbars
9552 * @cfg {Boolean} loadMask load mask when submit (default true)
9557 * @param {Object} config The config object
9561 Roo.bootstrap.Form = function(config){
9563 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9565 Roo.bootstrap.Form.popover.apply();
9569 * @event clientvalidation
9570 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9571 * @param {Form} this
9572 * @param {Boolean} valid true if the form has passed client-side validation
9574 clientvalidation: true,
9576 * @event beforeaction
9577 * Fires before any action is performed. Return false to cancel the action.
9578 * @param {Form} this
9579 * @param {Action} action The action to be performed
9583 * @event actionfailed
9584 * Fires when an action fails.
9585 * @param {Form} this
9586 * @param {Action} action The action that failed
9588 actionfailed : true,
9590 * @event actioncomplete
9591 * Fires when an action is completed.
9592 * @param {Form} this
9593 * @param {Action} action The action that completed
9595 actioncomplete : true
9599 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9602 * @cfg {String} method
9603 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9608 * The URL to use for form actions if one isn't supplied in the action options.
9611 * @cfg {Boolean} fileUpload
9612 * Set to true if this form is a file upload.
9616 * @cfg {Object} baseParams
9617 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9621 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9625 * @cfg {Sting} align (left|right) for navbar forms
9630 activeAction : null,
9633 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9634 * element by passing it or its id or mask the form itself by passing in true.
9637 waitMsgTarget : false,
9642 * @cfg {Boolean} errorMask (true|false) default false
9647 * @cfg {Number} maskOffset Default 100
9652 * @cfg {Boolean} maskBody
9656 getAutoCreate : function(){
9660 method : this.method || 'POST',
9661 id : this.id || Roo.id(),
9664 if (this.parent().xtype.match(/^Nav/)) {
9665 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9669 if (this.labelAlign == 'left' ) {
9670 cfg.cls += ' form-horizontal';
9676 initEvents : function()
9678 this.el.on('submit', this.onSubmit, this);
9679 // this was added as random key presses on the form where triggering form submit.
9680 this.el.on('keypress', function(e) {
9681 if (e.getCharCode() != 13) {
9684 // we might need to allow it for textareas.. and some other items.
9685 // check e.getTarget().
9687 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9691 Roo.log("keypress blocked");
9699 onSubmit : function(e){
9704 * Returns true if client-side validation on the form is successful.
9707 isValid : function(){
9708 var items = this.getItems();
9712 items.each(function(f){
9718 Roo.log('invalid field: ' + f.name);
9722 if(!target && f.el.isVisible(true)){
9728 if(this.errorMask && !valid){
9729 Roo.bootstrap.Form.popover.mask(this, target);
9736 * Returns true if any fields in this form have changed since their original load.
9739 isDirty : function(){
9741 var items = this.getItems();
9742 items.each(function(f){
9752 * Performs a predefined action (submit or load) or custom actions you define on this form.
9753 * @param {String} actionName The name of the action type
9754 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9755 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9756 * accept other config options):
9758 Property Type Description
9759 ---------------- --------------- ----------------------------------------------------------------------------------
9760 url String The url for the action (defaults to the form's url)
9761 method String The form method to use (defaults to the form's method, or POST if not defined)
9762 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9763 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9764 validate the form on the client (defaults to false)
9766 * @return {BasicForm} this
9768 doAction : function(action, options){
9769 if(typeof action == 'string'){
9770 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9772 if(this.fireEvent('beforeaction', this, action) !== false){
9773 this.beforeAction(action);
9774 action.run.defer(100, action);
9780 beforeAction : function(action){
9781 var o = action.options;
9786 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9788 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9791 // not really supported yet.. ??
9793 //if(this.waitMsgTarget === true){
9794 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9795 //}else if(this.waitMsgTarget){
9796 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9797 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9799 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9805 afterAction : function(action, success){
9806 this.activeAction = null;
9807 var o = action.options;
9812 Roo.get(document.body).unmask();
9818 //if(this.waitMsgTarget === true){
9819 // this.el.unmask();
9820 //}else if(this.waitMsgTarget){
9821 // this.waitMsgTarget.unmask();
9823 // Roo.MessageBox.updateProgress(1);
9824 // Roo.MessageBox.hide();
9831 Roo.callback(o.success, o.scope, [this, action]);
9832 this.fireEvent('actioncomplete', this, action);
9836 // failure condition..
9837 // we have a scenario where updates need confirming.
9838 // eg. if a locking scenario exists..
9839 // we look for { errors : { needs_confirm : true }} in the response.
9841 (typeof(action.result) != 'undefined') &&
9842 (typeof(action.result.errors) != 'undefined') &&
9843 (typeof(action.result.errors.needs_confirm) != 'undefined')
9846 Roo.log("not supported yet");
9849 Roo.MessageBox.confirm(
9850 "Change requires confirmation",
9851 action.result.errorMsg,
9856 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9866 Roo.callback(o.failure, o.scope, [this, action]);
9867 // show an error message if no failed handler is set..
9868 if (!this.hasListener('actionfailed')) {
9869 Roo.log("need to add dialog support");
9871 Roo.MessageBox.alert("Error",
9872 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9873 action.result.errorMsg :
9874 "Saving Failed, please check your entries or try again"
9879 this.fireEvent('actionfailed', this, action);
9884 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9885 * @param {String} id The value to search for
9888 findField : function(id){
9889 var items = this.getItems();
9890 var field = items.get(id);
9892 items.each(function(f){
9893 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9900 return field || null;
9903 * Mark fields in this form invalid in bulk.
9904 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9905 * @return {BasicForm} this
9907 markInvalid : function(errors){
9908 if(errors instanceof Array){
9909 for(var i = 0, len = errors.length; i < len; i++){
9910 var fieldError = errors[i];
9911 var f = this.findField(fieldError.id);
9913 f.markInvalid(fieldError.msg);
9919 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9920 field.markInvalid(errors[id]);
9924 //Roo.each(this.childForms || [], function (f) {
9925 // f.markInvalid(errors);
9932 * Set values for fields in this form in bulk.
9933 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9934 * @return {BasicForm} this
9936 setValues : function(values){
9937 if(values instanceof Array){ // array of objects
9938 for(var i = 0, len = values.length; i < len; i++){
9940 var f = this.findField(v.id);
9942 f.setValue(v.value);
9943 if(this.trackResetOnLoad){
9944 f.originalValue = f.getValue();
9948 }else{ // object hash
9951 if(typeof values[id] != 'function' && (field = this.findField(id))){
9953 if (field.setFromData &&
9955 field.displayField &&
9956 // combos' with local stores can
9957 // be queried via setValue()
9958 // to set their value..
9959 (field.store && !field.store.isLocal)
9963 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9964 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9965 field.setFromData(sd);
9967 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9969 field.setFromData(values);
9972 field.setValue(values[id]);
9976 if(this.trackResetOnLoad){
9977 field.originalValue = field.getValue();
9983 //Roo.each(this.childForms || [], function (f) {
9984 // f.setValues(values);
9991 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9992 * they are returned as an array.
9993 * @param {Boolean} asString
9996 getValues : function(asString){
9997 //if (this.childForms) {
9998 // copy values from the child forms
9999 // Roo.each(this.childForms, function (f) {
10000 // this.setValues(f.getValues());
10006 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10007 if(asString === true){
10010 return Roo.urlDecode(fs);
10014 * Returns the fields in this form as an object with key/value pairs.
10015 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10018 getFieldValues : function(with_hidden)
10020 var items = this.getItems();
10022 items.each(function(f){
10024 if (!f.getName()) {
10028 var v = f.getValue();
10030 if (f.inputType =='radio') {
10031 if (typeof(ret[f.getName()]) == 'undefined') {
10032 ret[f.getName()] = ''; // empty..
10035 if (!f.el.dom.checked) {
10039 v = f.el.dom.value;
10043 if(f.xtype == 'MoneyField'){
10044 ret[f.currencyName] = f.getCurrency();
10047 // not sure if this supported any more..
10048 if ((typeof(v) == 'object') && f.getRawValue) {
10049 v = f.getRawValue() ; // dates..
10051 // combo boxes where name != hiddenName...
10052 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10053 ret[f.name] = f.getRawValue();
10055 ret[f.getName()] = v;
10062 * Clears all invalid messages in this form.
10063 * @return {BasicForm} this
10065 clearInvalid : function(){
10066 var items = this.getItems();
10068 items.each(function(f){
10076 * Resets this form.
10077 * @return {BasicForm} this
10079 reset : function(){
10080 var items = this.getItems();
10081 items.each(function(f){
10085 Roo.each(this.childForms || [], function (f) {
10093 getItems : function()
10095 var r=new Roo.util.MixedCollection(false, function(o){
10096 return o.id || (o.id = Roo.id());
10098 var iter = function(el) {
10105 Roo.each(el.items,function(e) {
10114 hideFields : function(items)
10116 Roo.each(items, function(i){
10118 var f = this.findField(i);
10129 showFields : function(items)
10131 Roo.each(items, function(i){
10133 var f = this.findField(i);
10146 Roo.apply(Roo.bootstrap.Form, {
10162 intervalID : false,
10168 if(this.isApplied){
10173 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10174 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10175 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10176 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10179 this.maskEl.top.enableDisplayMode("block");
10180 this.maskEl.left.enableDisplayMode("block");
10181 this.maskEl.bottom.enableDisplayMode("block");
10182 this.maskEl.right.enableDisplayMode("block");
10184 this.toolTip = new Roo.bootstrap.Tooltip({
10185 cls : 'roo-form-error-popover',
10187 'left' : ['r-l', [-2,0], 'right'],
10188 'right' : ['l-r', [2,0], 'left'],
10189 'bottom' : ['tl-bl', [0,2], 'top'],
10190 'top' : [ 'bl-tl', [0,-2], 'bottom']
10194 this.toolTip.render(Roo.get(document.body));
10196 this.toolTip.el.enableDisplayMode("block");
10198 Roo.get(document.body).on('click', function(){
10202 Roo.get(document.body).on('touchstart', function(){
10206 this.isApplied = true
10209 mask : function(form, target)
10213 this.target = target;
10215 if(!this.form.errorMask || !target.el){
10219 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10221 Roo.log(scrollable);
10223 var ot = this.target.el.calcOffsetsTo(scrollable);
10225 var scrollTo = ot[1] - this.form.maskOffset;
10227 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10229 scrollable.scrollTo('top', scrollTo);
10231 var box = this.target.el.getBox();
10233 var zIndex = Roo.bootstrap.Modal.zIndex++;
10236 this.maskEl.top.setStyle('position', 'absolute');
10237 this.maskEl.top.setStyle('z-index', zIndex);
10238 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10239 this.maskEl.top.setLeft(0);
10240 this.maskEl.top.setTop(0);
10241 this.maskEl.top.show();
10243 this.maskEl.left.setStyle('position', 'absolute');
10244 this.maskEl.left.setStyle('z-index', zIndex);
10245 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10246 this.maskEl.left.setLeft(0);
10247 this.maskEl.left.setTop(box.y - this.padding);
10248 this.maskEl.left.show();
10250 this.maskEl.bottom.setStyle('position', 'absolute');
10251 this.maskEl.bottom.setStyle('z-index', zIndex);
10252 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10253 this.maskEl.bottom.setLeft(0);
10254 this.maskEl.bottom.setTop(box.bottom + this.padding);
10255 this.maskEl.bottom.show();
10257 this.maskEl.right.setStyle('position', 'absolute');
10258 this.maskEl.right.setStyle('z-index', zIndex);
10259 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10260 this.maskEl.right.setLeft(box.right + this.padding);
10261 this.maskEl.right.setTop(box.y - this.padding);
10262 this.maskEl.right.show();
10264 this.toolTip.bindEl = this.target.el;
10266 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10268 var tip = this.target.blankText;
10270 if(this.target.getValue() !== '' ) {
10272 if (this.target.invalidText.length) {
10273 tip = this.target.invalidText;
10274 } else if (this.target.regexText.length){
10275 tip = this.target.regexText;
10279 this.toolTip.show(tip);
10281 this.intervalID = window.setInterval(function() {
10282 Roo.bootstrap.Form.popover.unmask();
10285 window.onwheel = function(){ return false;};
10287 (function(){ this.isMasked = true; }).defer(500, this);
10291 unmask : function()
10293 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10297 this.maskEl.top.setStyle('position', 'absolute');
10298 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10299 this.maskEl.top.hide();
10301 this.maskEl.left.setStyle('position', 'absolute');
10302 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10303 this.maskEl.left.hide();
10305 this.maskEl.bottom.setStyle('position', 'absolute');
10306 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10307 this.maskEl.bottom.hide();
10309 this.maskEl.right.setStyle('position', 'absolute');
10310 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10311 this.maskEl.right.hide();
10313 this.toolTip.hide();
10315 this.toolTip.el.hide();
10317 window.onwheel = function(){ return true;};
10319 if(this.intervalID){
10320 window.clearInterval(this.intervalID);
10321 this.intervalID = false;
10324 this.isMasked = false;
10334 * Ext JS Library 1.1.1
10335 * Copyright(c) 2006-2007, Ext JS, LLC.
10337 * Originally Released Under LGPL - original licence link has changed is not relivant.
10340 * <script type="text/javascript">
10343 * @class Roo.form.VTypes
10344 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10347 Roo.form.VTypes = function(){
10348 // closure these in so they are only created once.
10349 var alpha = /^[a-zA-Z_]+$/;
10350 var alphanum = /^[a-zA-Z0-9_]+$/;
10351 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10352 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10354 // All these messages and functions are configurable
10357 * The function used to validate email addresses
10358 * @param {String} value The email address
10360 'email' : function(v){
10361 return email.test(v);
10364 * The error text to display when the email validation function returns false
10367 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10369 * The keystroke filter mask to be applied on email input
10372 'emailMask' : /[a-z0-9_\.\-@]/i,
10375 * The function used to validate URLs
10376 * @param {String} value The URL
10378 'url' : function(v){
10379 return url.test(v);
10382 * The error text to display when the url validation function returns false
10385 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10388 * The function used to validate alpha values
10389 * @param {String} value The value
10391 'alpha' : function(v){
10392 return alpha.test(v);
10395 * The error text to display when the alpha validation function returns false
10398 'alphaText' : 'This field should only contain letters and _',
10400 * The keystroke filter mask to be applied on alpha input
10403 'alphaMask' : /[a-z_]/i,
10406 * The function used to validate alphanumeric values
10407 * @param {String} value The value
10409 'alphanum' : function(v){
10410 return alphanum.test(v);
10413 * The error text to display when the alphanumeric validation function returns false
10416 'alphanumText' : 'This field should only contain letters, numbers and _',
10418 * The keystroke filter mask to be applied on alphanumeric input
10421 'alphanumMask' : /[a-z0-9_]/i
10431 * @class Roo.bootstrap.Input
10432 * @extends Roo.bootstrap.Component
10433 * Bootstrap Input class
10434 * @cfg {Boolean} disabled is it disabled
10435 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10436 * @cfg {String} name name of the input
10437 * @cfg {string} fieldLabel - the label associated
10438 * @cfg {string} placeholder - placeholder to put in text.
10439 * @cfg {string} before - input group add on before
10440 * @cfg {string} after - input group add on after
10441 * @cfg {string} size - (lg|sm) or leave empty..
10442 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10443 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10444 * @cfg {Number} md colspan out of 12 for computer-sized screens
10445 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10446 * @cfg {string} value default value of the input
10447 * @cfg {Number} labelWidth set the width of label
10448 * @cfg {Number} labellg set the width of label (1-12)
10449 * @cfg {Number} labelmd set the width of label (1-12)
10450 * @cfg {Number} labelsm set the width of label (1-12)
10451 * @cfg {Number} labelxs set the width of label (1-12)
10452 * @cfg {String} labelAlign (top|left)
10453 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10454 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10455 * @cfg {String} indicatorpos (left|right) default left
10456 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10457 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10458 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10460 * @cfg {String} align (left|center|right) Default left
10461 * @cfg {Boolean} forceFeedback (true|false) Default false
10464 * Create a new Input
10465 * @param {Object} config The config object
10468 Roo.bootstrap.Input = function(config){
10470 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10475 * Fires when this field receives input focus.
10476 * @param {Roo.form.Field} this
10481 * Fires when this field loses input focus.
10482 * @param {Roo.form.Field} this
10486 * @event specialkey
10487 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10488 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10489 * @param {Roo.form.Field} this
10490 * @param {Roo.EventObject} e The event object
10495 * Fires just before the field blurs if the field value has changed.
10496 * @param {Roo.form.Field} this
10497 * @param {Mixed} newValue The new value
10498 * @param {Mixed} oldValue The original value
10503 * Fires after the field has been marked as invalid.
10504 * @param {Roo.form.Field} this
10505 * @param {String} msg The validation message
10510 * Fires after the field has been validated with no errors.
10511 * @param {Roo.form.Field} this
10516 * Fires after the key up
10517 * @param {Roo.form.Field} this
10518 * @param {Roo.EventObject} e The event Object
10523 * Fires after the user pastes into input
10524 * @param {Roo.form.Field} this
10525 * @param {Roo.EventObject} e The event Object
10531 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10533 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10534 automatic validation (defaults to "keyup").
10536 validationEvent : "keyup",
10538 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10540 validateOnBlur : true,
10542 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10544 validationDelay : 250,
10546 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10548 focusClass : "x-form-focus", // not needed???
10552 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10554 invalidClass : "has-warning",
10557 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10559 validClass : "has-success",
10562 * @cfg {Boolean} hasFeedback (true|false) default true
10564 hasFeedback : true,
10567 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10569 invalidFeedbackClass : "glyphicon-warning-sign",
10572 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10574 validFeedbackClass : "glyphicon-ok",
10577 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10579 selectOnFocus : false,
10582 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10586 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10591 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10593 disableKeyFilter : false,
10596 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10600 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10604 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10606 blankText : "Please complete this mandatory field",
10609 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10613 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10615 maxLength : Number.MAX_VALUE,
10617 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10619 minLengthText : "The minimum length for this field is {0}",
10621 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10623 maxLengthText : "The maximum length for this field is {0}",
10627 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10628 * If available, this function will be called only after the basic validators all return true, and will be passed the
10629 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10633 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10634 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10635 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10639 * @cfg {String} regexText -- Depricated - use Invalid Text
10644 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10650 autocomplete: false,
10654 inputType : 'text',
10657 placeholder: false,
10662 preventMark: false,
10663 isFormField : true,
10666 labelAlign : false,
10669 formatedValue : false,
10670 forceFeedback : false,
10672 indicatorpos : 'left',
10682 parentLabelAlign : function()
10685 while (parent.parent()) {
10686 parent = parent.parent();
10687 if (typeof(parent.labelAlign) !='undefined') {
10688 return parent.labelAlign;
10695 getAutoCreate : function()
10697 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10703 if(this.inputType != 'hidden'){
10704 cfg.cls = 'form-group' //input-group
10710 type : this.inputType,
10711 value : this.value,
10712 cls : 'form-control',
10713 placeholder : this.placeholder || '',
10714 autocomplete : this.autocomplete || 'new-password'
10716 if (this.inputType == 'file') {
10717 input.style = 'overflow:hidden'; // why not in CSS?
10720 if(this.capture.length){
10721 input.capture = this.capture;
10724 if(this.accept.length){
10725 input.accept = this.accept + "/*";
10729 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10732 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10733 input.maxLength = this.maxLength;
10736 if (this.disabled) {
10737 input.disabled=true;
10740 if (this.readOnly) {
10741 input.readonly=true;
10745 input.name = this.name;
10749 input.cls += ' input-' + this.size;
10753 ['xs','sm','md','lg'].map(function(size){
10754 if (settings[size]) {
10755 cfg.cls += ' col-' + size + '-' + settings[size];
10759 var inputblock = input;
10763 cls: 'glyphicon form-control-feedback'
10766 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10769 cls : 'has-feedback',
10777 if (this.before || this.after) {
10780 cls : 'input-group',
10784 if (this.before && typeof(this.before) == 'string') {
10786 inputblock.cn.push({
10788 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10792 if (this.before && typeof(this.before) == 'object') {
10793 this.before = Roo.factory(this.before);
10795 inputblock.cn.push({
10797 cls : 'roo-input-before input-group-prepend input-group-' +
10798 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10802 inputblock.cn.push(input);
10804 if (this.after && typeof(this.after) == 'string') {
10805 inputblock.cn.push({
10807 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10811 if (this.after && typeof(this.after) == 'object') {
10812 this.after = Roo.factory(this.after);
10814 inputblock.cn.push({
10816 cls : 'roo-input-after input-group-append input-group-' +
10817 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10821 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10822 inputblock.cls += ' has-feedback';
10823 inputblock.cn.push(feedback);
10828 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10829 tooltip : 'This field is required'
10831 if (this.allowBlank ) {
10832 indicator.style = this.allowBlank ? ' display:none' : '';
10834 if (align ==='left' && this.fieldLabel.length) {
10836 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10843 cls : 'control-label col-form-label',
10844 html : this.fieldLabel
10855 var labelCfg = cfg.cn[1];
10856 var contentCfg = cfg.cn[2];
10858 if(this.indicatorpos == 'right'){
10863 cls : 'control-label col-form-label',
10867 html : this.fieldLabel
10881 labelCfg = cfg.cn[0];
10882 contentCfg = cfg.cn[1];
10886 if(this.labelWidth > 12){
10887 labelCfg.style = "width: " + this.labelWidth + 'px';
10890 if(this.labelWidth < 13 && this.labelmd == 0){
10891 this.labelmd = this.labelWidth;
10894 if(this.labellg > 0){
10895 labelCfg.cls += ' col-lg-' + this.labellg;
10896 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10899 if(this.labelmd > 0){
10900 labelCfg.cls += ' col-md-' + this.labelmd;
10901 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10904 if(this.labelsm > 0){
10905 labelCfg.cls += ' col-sm-' + this.labelsm;
10906 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10909 if(this.labelxs > 0){
10910 labelCfg.cls += ' col-xs-' + this.labelxs;
10911 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10915 } else if ( this.fieldLabel.length) {
10922 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10923 tooltip : 'This field is required',
10924 style : this.allowBlank ? ' display:none' : ''
10928 //cls : 'input-group-addon',
10929 html : this.fieldLabel
10937 if(this.indicatorpos == 'right'){
10942 //cls : 'input-group-addon',
10943 html : this.fieldLabel
10948 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10949 tooltip : 'This field is required',
10950 style : this.allowBlank ? ' display:none' : ''
10970 if (this.parentType === 'Navbar' && this.parent().bar) {
10971 cfg.cls += ' navbar-form';
10974 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10975 // on BS4 we do this only if not form
10976 cfg.cls += ' navbar-form';
10984 * return the real input element.
10986 inputEl: function ()
10988 return this.el.select('input.form-control',true).first();
10991 tooltipEl : function()
10993 return this.inputEl();
10996 indicatorEl : function()
10998 if (Roo.bootstrap.version == 4) {
10999 return false; // not enabled in v4 yet.
11002 var indicator = this.el.select('i.roo-required-indicator',true).first();
11012 setDisabled : function(v)
11014 var i = this.inputEl().dom;
11016 i.removeAttribute('disabled');
11020 i.setAttribute('disabled','true');
11022 initEvents : function()
11025 this.inputEl().on("keydown" , this.fireKey, this);
11026 this.inputEl().on("focus", this.onFocus, this);
11027 this.inputEl().on("blur", this.onBlur, this);
11029 this.inputEl().relayEvent('keyup', this);
11030 this.inputEl().relayEvent('paste', this);
11032 this.indicator = this.indicatorEl();
11034 if(this.indicator){
11035 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11038 // reference to original value for reset
11039 this.originalValue = this.getValue();
11040 //Roo.form.TextField.superclass.initEvents.call(this);
11041 if(this.validationEvent == 'keyup'){
11042 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11043 this.inputEl().on('keyup', this.filterValidation, this);
11045 else if(this.validationEvent !== false){
11046 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11049 if(this.selectOnFocus){
11050 this.on("focus", this.preFocus, this);
11053 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11054 this.inputEl().on("keypress", this.filterKeys, this);
11056 this.inputEl().relayEvent('keypress', this);
11059 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11060 this.el.on("click", this.autoSize, this);
11063 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11064 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11067 if (typeof(this.before) == 'object') {
11068 this.before.render(this.el.select('.roo-input-before',true).first());
11070 if (typeof(this.after) == 'object') {
11071 this.after.render(this.el.select('.roo-input-after',true).first());
11074 this.inputEl().on('change', this.onChange, this);
11077 filterValidation : function(e){
11078 if(!e.isNavKeyPress()){
11079 this.validationTask.delay(this.validationDelay);
11083 * Validates the field value
11084 * @return {Boolean} True if the value is valid, else false
11086 validate : function(){
11087 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11088 if(this.disabled || this.validateValue(this.getRawValue())){
11093 this.markInvalid();
11099 * Validates a value according to the field's validation rules and marks the field as invalid
11100 * if the validation fails
11101 * @param {Mixed} value The value to validate
11102 * @return {Boolean} True if the value is valid, else false
11104 validateValue : function(value)
11106 if(this.getVisibilityEl().hasClass('hidden')){
11110 if(value.length < 1) { // if it's blank
11111 if(this.allowBlank){
11117 if(value.length < this.minLength){
11120 if(value.length > this.maxLength){
11124 var vt = Roo.form.VTypes;
11125 if(!vt[this.vtype](value, this)){
11129 if(typeof this.validator == "function"){
11130 var msg = this.validator(value);
11134 if (typeof(msg) == 'string') {
11135 this.invalidText = msg;
11139 if(this.regex && !this.regex.test(value)){
11147 fireKey : function(e){
11148 //Roo.log('field ' + e.getKey());
11149 if(e.isNavKeyPress()){
11150 this.fireEvent("specialkey", this, e);
11153 focus : function (selectText){
11155 this.inputEl().focus();
11156 if(selectText === true){
11157 this.inputEl().dom.select();
11163 onFocus : function(){
11164 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11165 // this.el.addClass(this.focusClass);
11167 if(!this.hasFocus){
11168 this.hasFocus = true;
11169 this.startValue = this.getValue();
11170 this.fireEvent("focus", this);
11174 beforeBlur : Roo.emptyFn,
11178 onBlur : function(){
11180 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11181 //this.el.removeClass(this.focusClass);
11183 this.hasFocus = false;
11184 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11187 var v = this.getValue();
11188 if(String(v) !== String(this.startValue)){
11189 this.fireEvent('change', this, v, this.startValue);
11191 this.fireEvent("blur", this);
11194 onChange : function(e)
11196 var v = this.getValue();
11197 if(String(v) !== String(this.startValue)){
11198 this.fireEvent('change', this, v, this.startValue);
11204 * Resets the current field value to the originally loaded value and clears any validation messages
11206 reset : function(){
11207 this.setValue(this.originalValue);
11211 * Returns the name of the field
11212 * @return {Mixed} name The name field
11214 getName: function(){
11218 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11219 * @return {Mixed} value The field value
11221 getValue : function(){
11223 var v = this.inputEl().getValue();
11228 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11229 * @return {Mixed} value The field value
11231 getRawValue : function(){
11232 var v = this.inputEl().getValue();
11238 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11239 * @param {Mixed} value The value to set
11241 setRawValue : function(v){
11242 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11245 selectText : function(start, end){
11246 var v = this.getRawValue();
11248 start = start === undefined ? 0 : start;
11249 end = end === undefined ? v.length : end;
11250 var d = this.inputEl().dom;
11251 if(d.setSelectionRange){
11252 d.setSelectionRange(start, end);
11253 }else if(d.createTextRange){
11254 var range = d.createTextRange();
11255 range.moveStart("character", start);
11256 range.moveEnd("character", v.length-end);
11263 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11264 * @param {Mixed} value The value to set
11266 setValue : function(v){
11269 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11275 processValue : function(value){
11276 if(this.stripCharsRe){
11277 var newValue = value.replace(this.stripCharsRe, '');
11278 if(newValue !== value){
11279 this.setRawValue(newValue);
11286 preFocus : function(){
11288 if(this.selectOnFocus){
11289 this.inputEl().dom.select();
11292 filterKeys : function(e){
11293 var k = e.getKey();
11294 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11297 var c = e.getCharCode(), cc = String.fromCharCode(c);
11298 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11301 if(!this.maskRe.test(cc)){
11306 * Clear any invalid styles/messages for this field
11308 clearInvalid : function(){
11310 if(!this.el || this.preventMark){ // not rendered
11315 this.el.removeClass([this.invalidClass, 'is-invalid']);
11317 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11319 var feedback = this.el.select('.form-control-feedback', true).first();
11322 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11327 if(this.indicator){
11328 this.indicator.removeClass('visible');
11329 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11332 this.fireEvent('valid', this);
11336 * Mark this field as valid
11338 markValid : function()
11340 if(!this.el || this.preventMark){ // not rendered...
11344 this.el.removeClass([this.invalidClass, this.validClass]);
11345 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11347 var feedback = this.el.select('.form-control-feedback', true).first();
11350 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11353 if(this.indicator){
11354 this.indicator.removeClass('visible');
11355 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11363 if(this.allowBlank && !this.getRawValue().length){
11366 if (Roo.bootstrap.version == 3) {
11367 this.el.addClass(this.validClass);
11369 this.inputEl().addClass('is-valid');
11372 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11374 var feedback = this.el.select('.form-control-feedback', true).first();
11377 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11378 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11383 this.fireEvent('valid', this);
11387 * Mark this field as invalid
11388 * @param {String} msg The validation message
11390 markInvalid : function(msg)
11392 if(!this.el || this.preventMark){ // not rendered
11396 this.el.removeClass([this.invalidClass, this.validClass]);
11397 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11399 var feedback = this.el.select('.form-control-feedback', true).first();
11402 this.el.select('.form-control-feedback', true).first().removeClass(
11403 [this.invalidFeedbackClass, this.validFeedbackClass]);
11410 if(this.allowBlank && !this.getRawValue().length){
11414 if(this.indicator){
11415 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11416 this.indicator.addClass('visible');
11418 if (Roo.bootstrap.version == 3) {
11419 this.el.addClass(this.invalidClass);
11421 this.inputEl().addClass('is-invalid');
11426 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11428 var feedback = this.el.select('.form-control-feedback', true).first();
11431 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11433 if(this.getValue().length || this.forceFeedback){
11434 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11441 this.fireEvent('invalid', this, msg);
11444 SafariOnKeyDown : function(event)
11446 // this is a workaround for a password hang bug on chrome/ webkit.
11447 if (this.inputEl().dom.type != 'password') {
11451 var isSelectAll = false;
11453 if(this.inputEl().dom.selectionEnd > 0){
11454 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11456 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11457 event.preventDefault();
11462 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11464 event.preventDefault();
11465 // this is very hacky as keydown always get's upper case.
11467 var cc = String.fromCharCode(event.getCharCode());
11468 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11472 adjustWidth : function(tag, w){
11473 tag = tag.toLowerCase();
11474 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11475 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11476 if(tag == 'input'){
11479 if(tag == 'textarea'){
11482 }else if(Roo.isOpera){
11483 if(tag == 'input'){
11486 if(tag == 'textarea'){
11494 setFieldLabel : function(v)
11496 if(!this.rendered){
11500 if(this.indicatorEl()){
11501 var ar = this.el.select('label > span',true);
11503 if (ar.elements.length) {
11504 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11505 this.fieldLabel = v;
11509 var br = this.el.select('label',true);
11511 if(br.elements.length) {
11512 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11513 this.fieldLabel = v;
11517 Roo.log('Cannot Found any of label > span || label in input');
11521 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11522 this.fieldLabel = v;
11537 * @class Roo.bootstrap.TextArea
11538 * @extends Roo.bootstrap.Input
11539 * Bootstrap TextArea class
11540 * @cfg {Number} cols Specifies the visible width of a text area
11541 * @cfg {Number} rows Specifies the visible number of lines in a text area
11542 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11543 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11544 * @cfg {string} html text
11547 * Create a new TextArea
11548 * @param {Object} config The config object
11551 Roo.bootstrap.TextArea = function(config){
11552 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11556 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11566 getAutoCreate : function(){
11568 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11574 if(this.inputType != 'hidden'){
11575 cfg.cls = 'form-group' //input-group
11583 value : this.value || '',
11584 html: this.html || '',
11585 cls : 'form-control',
11586 placeholder : this.placeholder || ''
11590 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11591 input.maxLength = this.maxLength;
11595 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11599 input.cols = this.cols;
11602 if (this.readOnly) {
11603 input.readonly = true;
11607 input.name = this.name;
11611 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11615 ['xs','sm','md','lg'].map(function(size){
11616 if (settings[size]) {
11617 cfg.cls += ' col-' + size + '-' + settings[size];
11621 var inputblock = input;
11623 if(this.hasFeedback && !this.allowBlank){
11627 cls: 'glyphicon form-control-feedback'
11631 cls : 'has-feedback',
11640 if (this.before || this.after) {
11643 cls : 'input-group',
11647 inputblock.cn.push({
11649 cls : 'input-group-addon',
11654 inputblock.cn.push(input);
11656 if(this.hasFeedback && !this.allowBlank){
11657 inputblock.cls += ' has-feedback';
11658 inputblock.cn.push(feedback);
11662 inputblock.cn.push({
11664 cls : 'input-group-addon',
11671 if (align ==='left' && this.fieldLabel.length) {
11676 cls : 'control-label',
11677 html : this.fieldLabel
11688 if(this.labelWidth > 12){
11689 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11692 if(this.labelWidth < 13 && this.labelmd == 0){
11693 this.labelmd = this.labelWidth;
11696 if(this.labellg > 0){
11697 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11698 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11701 if(this.labelmd > 0){
11702 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11703 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11706 if(this.labelsm > 0){
11707 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11708 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11711 if(this.labelxs > 0){
11712 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11713 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11716 } else if ( this.fieldLabel.length) {
11721 //cls : 'input-group-addon',
11722 html : this.fieldLabel
11740 if (this.disabled) {
11741 input.disabled=true;
11748 * return the real textarea element.
11750 inputEl: function ()
11752 return this.el.select('textarea.form-control',true).first();
11756 * Clear any invalid styles/messages for this field
11758 clearInvalid : function()
11761 if(!this.el || this.preventMark){ // not rendered
11765 var label = this.el.select('label', true).first();
11766 var icon = this.el.select('i.fa-star', true).first();
11771 this.el.removeClass( this.validClass);
11772 this.inputEl().removeClass('is-invalid');
11774 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11776 var feedback = this.el.select('.form-control-feedback', true).first();
11779 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11784 this.fireEvent('valid', this);
11788 * Mark this field as valid
11790 markValid : function()
11792 if(!this.el || this.preventMark){ // not rendered
11796 this.el.removeClass([this.invalidClass, this.validClass]);
11797 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11799 var feedback = this.el.select('.form-control-feedback', true).first();
11802 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11805 if(this.disabled || this.allowBlank){
11809 var label = this.el.select('label', true).first();
11810 var icon = this.el.select('i.fa-star', true).first();
11815 if (Roo.bootstrap.version == 3) {
11816 this.el.addClass(this.validClass);
11818 this.inputEl().addClass('is-valid');
11822 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11824 var feedback = this.el.select('.form-control-feedback', true).first();
11827 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11828 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11833 this.fireEvent('valid', this);
11837 * Mark this field as invalid
11838 * @param {String} msg The validation message
11840 markInvalid : function(msg)
11842 if(!this.el || this.preventMark){ // not rendered
11846 this.el.removeClass([this.invalidClass, this.validClass]);
11847 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11849 var feedback = this.el.select('.form-control-feedback', true).first();
11852 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11855 if(this.disabled || this.allowBlank){
11859 var label = this.el.select('label', true).first();
11860 var icon = this.el.select('i.fa-star', true).first();
11862 if(!this.getValue().length && label && !icon){
11863 this.el.createChild({
11865 cls : 'text-danger fa fa-lg fa-star',
11866 tooltip : 'This field is required',
11867 style : 'margin-right:5px;'
11871 if (Roo.bootstrap.version == 3) {
11872 this.el.addClass(this.invalidClass);
11874 this.inputEl().addClass('is-invalid');
11877 // fixme ... this may be depricated need to test..
11878 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11880 var feedback = this.el.select('.form-control-feedback', true).first();
11883 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11885 if(this.getValue().length || this.forceFeedback){
11886 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11893 this.fireEvent('invalid', this, msg);
11901 * trigger field - base class for combo..
11906 * @class Roo.bootstrap.TriggerField
11907 * @extends Roo.bootstrap.Input
11908 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11909 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11910 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11911 * for which you can provide a custom implementation. For example:
11913 var trigger = new Roo.bootstrap.TriggerField();
11914 trigger.onTriggerClick = myTriggerFn;
11915 trigger.applyTo('my-field');
11918 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11919 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11920 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11921 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11922 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11925 * Create a new TriggerField.
11926 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11927 * to the base TextField)
11929 Roo.bootstrap.TriggerField = function(config){
11930 this.mimicing = false;
11931 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11934 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11936 * @cfg {String} triggerClass A CSS class to apply to the trigger
11939 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11944 * @cfg {Boolean} removable (true|false) special filter default false
11948 /** @cfg {Boolean} grow @hide */
11949 /** @cfg {Number} growMin @hide */
11950 /** @cfg {Number} growMax @hide */
11956 autoSize: Roo.emptyFn,
11960 deferHeight : true,
11963 actionMode : 'wrap',
11968 getAutoCreate : function(){
11970 var align = this.labelAlign || this.parentLabelAlign();
11975 cls: 'form-group' //input-group
11982 type : this.inputType,
11983 cls : 'form-control',
11984 autocomplete: 'new-password',
11985 placeholder : this.placeholder || ''
11989 input.name = this.name;
11992 input.cls += ' input-' + this.size;
11995 if (this.disabled) {
11996 input.disabled=true;
11999 var inputblock = input;
12001 if(this.hasFeedback && !this.allowBlank){
12005 cls: 'glyphicon form-control-feedback'
12008 if(this.removable && !this.editable ){
12010 cls : 'has-feedback',
12016 cls : 'roo-combo-removable-btn close'
12023 cls : 'has-feedback',
12032 if(this.removable && !this.editable ){
12034 cls : 'roo-removable',
12040 cls : 'roo-combo-removable-btn close'
12047 if (this.before || this.after) {
12050 cls : 'input-group',
12054 inputblock.cn.push({
12056 cls : 'input-group-addon input-group-prepend input-group-text',
12061 inputblock.cn.push(input);
12063 if(this.hasFeedback && !this.allowBlank){
12064 inputblock.cls += ' has-feedback';
12065 inputblock.cn.push(feedback);
12069 inputblock.cn.push({
12071 cls : 'input-group-addon input-group-append input-group-text',
12080 var ibwrap = inputblock;
12085 cls: 'roo-select2-choices',
12089 cls: 'roo-select2-search-field',
12101 cls: 'roo-select2-container input-group',
12106 cls: 'form-hidden-field'
12112 if(!this.multiple && this.showToggleBtn){
12118 if (this.caret != false) {
12121 cls: 'fa fa-' + this.caret
12128 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12130 Roo.bootstrap.version == 3 ? caret : '',
12133 cls: 'combobox-clear',
12147 combobox.cls += ' roo-select2-container-multi';
12151 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12152 tooltip : 'This field is required'
12154 if (Roo.bootstrap.version == 4) {
12157 style : 'display:none'
12162 if (align ==='left' && this.fieldLabel.length) {
12164 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12171 cls : 'control-label',
12172 html : this.fieldLabel
12184 var labelCfg = cfg.cn[1];
12185 var contentCfg = cfg.cn[2];
12187 if(this.indicatorpos == 'right'){
12192 cls : 'control-label',
12196 html : this.fieldLabel
12210 labelCfg = cfg.cn[0];
12211 contentCfg = cfg.cn[1];
12214 if(this.labelWidth > 12){
12215 labelCfg.style = "width: " + this.labelWidth + 'px';
12218 if(this.labelWidth < 13 && this.labelmd == 0){
12219 this.labelmd = this.labelWidth;
12222 if(this.labellg > 0){
12223 labelCfg.cls += ' col-lg-' + this.labellg;
12224 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12227 if(this.labelmd > 0){
12228 labelCfg.cls += ' col-md-' + this.labelmd;
12229 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12232 if(this.labelsm > 0){
12233 labelCfg.cls += ' col-sm-' + this.labelsm;
12234 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12237 if(this.labelxs > 0){
12238 labelCfg.cls += ' col-xs-' + this.labelxs;
12239 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12242 } else if ( this.fieldLabel.length) {
12243 // Roo.log(" label");
12248 //cls : 'input-group-addon',
12249 html : this.fieldLabel
12257 if(this.indicatorpos == 'right'){
12265 html : this.fieldLabel
12279 // Roo.log(" no label && no align");
12286 ['xs','sm','md','lg'].map(function(size){
12287 if (settings[size]) {
12288 cfg.cls += ' col-' + size + '-' + settings[size];
12299 onResize : function(w, h){
12300 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12301 // if(typeof w == 'number'){
12302 // var x = w - this.trigger.getWidth();
12303 // this.inputEl().setWidth(this.adjustWidth('input', x));
12304 // this.trigger.setStyle('left', x+'px');
12309 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12312 getResizeEl : function(){
12313 return this.inputEl();
12317 getPositionEl : function(){
12318 return this.inputEl();
12322 alignErrorIcon : function(){
12323 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12327 initEvents : function(){
12331 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12332 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12333 if(!this.multiple && this.showToggleBtn){
12334 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12335 if(this.hideTrigger){
12336 this.trigger.setDisplayed(false);
12338 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12342 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12345 if(this.removable && !this.editable && !this.tickable){
12346 var close = this.closeTriggerEl();
12349 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12350 close.on('click', this.removeBtnClick, this, close);
12354 //this.trigger.addClassOnOver('x-form-trigger-over');
12355 //this.trigger.addClassOnClick('x-form-trigger-click');
12358 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12362 closeTriggerEl : function()
12364 var close = this.el.select('.roo-combo-removable-btn', true).first();
12365 return close ? close : false;
12368 removeBtnClick : function(e, h, el)
12370 e.preventDefault();
12372 if(this.fireEvent("remove", this) !== false){
12374 this.fireEvent("afterremove", this)
12378 createList : function()
12380 this.list = Roo.get(document.body).createChild({
12381 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12382 cls: 'typeahead typeahead-long dropdown-menu shadow',
12383 style: 'display:none'
12386 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12391 initTrigger : function(){
12396 onDestroy : function(){
12398 this.trigger.removeAllListeners();
12399 // this.trigger.remove();
12402 // this.wrap.remove();
12404 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12408 onFocus : function(){
12409 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12411 if(!this.mimicing){
12412 this.wrap.addClass('x-trigger-wrap-focus');
12413 this.mimicing = true;
12414 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12415 if(this.monitorTab){
12416 this.el.on("keydown", this.checkTab, this);
12423 checkTab : function(e){
12424 if(e.getKey() == e.TAB){
12425 this.triggerBlur();
12430 onBlur : function(){
12435 mimicBlur : function(e, t){
12437 if(!this.wrap.contains(t) && this.validateBlur()){
12438 this.triggerBlur();
12444 triggerBlur : function(){
12445 this.mimicing = false;
12446 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12447 if(this.monitorTab){
12448 this.el.un("keydown", this.checkTab, this);
12450 //this.wrap.removeClass('x-trigger-wrap-focus');
12451 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12455 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12456 validateBlur : function(e, t){
12461 onDisable : function(){
12462 this.inputEl().dom.disabled = true;
12463 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12465 // this.wrap.addClass('x-item-disabled');
12470 onEnable : function(){
12471 this.inputEl().dom.disabled = false;
12472 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12474 // this.el.removeClass('x-item-disabled');
12479 onShow : function(){
12480 var ae = this.getActionEl();
12483 ae.dom.style.display = '';
12484 ae.dom.style.visibility = 'visible';
12490 onHide : function(){
12491 var ae = this.getActionEl();
12492 ae.dom.style.display = 'none';
12496 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12497 * by an implementing function.
12499 * @param {EventObject} e
12501 onTriggerClick : Roo.emptyFn
12509 * @class Roo.bootstrap.CardUploader
12510 * @extends Roo.bootstrap.Button
12511 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12512 * @cfg {Number} errorTimeout default 3000
12513 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12514 * @cfg {Array} html The button text.
12518 * Create a new CardUploader
12519 * @param {Object} config The config object
12522 Roo.bootstrap.CardUploader = function(config){
12526 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12529 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12537 * When a image is clicked on - and needs to display a slideshow or similar..
12538 * @param {Roo.bootstrap.Card} this
12539 * @param {Object} The image information data
12545 * When a the download link is clicked
12546 * @param {Roo.bootstrap.Card} this
12547 * @param {Object} The image information data contains
12554 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12557 errorTimeout : 3000,
12561 fileCollection : false,
12564 getAutoCreate : function()
12568 cls :'form-group' ,
12573 //cls : 'input-group-addon',
12574 html : this.fieldLabel
12582 value : this.value,
12583 cls : 'd-none form-control'
12588 multiple : 'multiple',
12590 cls : 'd-none roo-card-upload-selector'
12594 cls : 'roo-card-uploader-button-container w-100 mb-2'
12597 cls : 'card-columns roo-card-uploader-container'
12607 getChildContainer : function() /// what children are added to.
12609 return this.containerEl;
12612 getButtonContainer : function() /// what children are added to.
12614 return this.el.select(".roo-card-uploader-button-container").first();
12617 initEvents : function()
12620 Roo.bootstrap.Input.prototype.initEvents.call(this);
12624 xns: Roo.bootstrap,
12627 container_method : 'getButtonContainer' ,
12628 html : this.html, // fix changable?
12631 'click' : function(btn, e) {
12640 this.urlAPI = (window.createObjectURL && window) ||
12641 (window.URL && URL.revokeObjectURL && URL) ||
12642 (window.webkitURL && webkitURL);
12647 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12649 this.selectorEl.on('change', this.onFileSelected, this);
12652 this.images.forEach(function(img) {
12655 this.images = false;
12657 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12663 onClick : function(e)
12665 e.preventDefault();
12667 this.selectorEl.dom.click();
12671 onFileSelected : function(e)
12673 e.preventDefault();
12675 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12679 Roo.each(this.selectorEl.dom.files, function(file){
12680 this.addFile(file);
12689 addFile : function(file)
12692 if(typeof(file) === 'string'){
12693 throw "Add file by name?"; // should not happen
12697 if(!file || !this.urlAPI){
12707 var url = _this.urlAPI.createObjectURL( file);
12710 id : Roo.bootstrap.CardUploader.ID--,
12711 is_uploaded : false,
12715 mimetype : file.type,
12723 * addCard - add an Attachment to the uploader
12724 * @param data - the data about the image to upload
12728 title : "Title of file",
12729 is_uploaded : false,
12730 src : "http://.....",
12731 srcfile : { the File upload object },
12732 mimetype : file.type,
12735 .. any other data...
12741 addCard : function (data)
12743 // hidden input element?
12744 // if the file is not an image...
12745 //then we need to use something other that and header_image
12750 xns : Roo.bootstrap,
12751 xtype : 'CardFooter',
12754 xns : Roo.bootstrap,
12760 xns : Roo.bootstrap,
12762 html : String.format("<small>{0}</small>", data.title),
12763 cls : 'col-10 text-left',
12768 click : function() {
12770 t.fireEvent( "download", t, data );
12776 xns : Roo.bootstrap,
12778 style: 'max-height: 28px; ',
12784 click : function() {
12785 t.removeCard(data.id)
12797 var cn = this.addxtype(
12800 xns : Roo.bootstrap,
12803 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12804 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12805 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12810 initEvents : function() {
12811 Roo.bootstrap.Card.prototype.initEvents.call(this);
12813 this.imgEl = this.el.select('.card-img-top').first();
12815 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12816 this.imgEl.set({ 'pointer' : 'cursor' });
12819 this.getCardFooter().addClass('p-1');
12826 // dont' really need ot update items.
12827 // this.items.push(cn);
12828 this.fileCollection.add(cn);
12830 if (!data.srcfile) {
12831 this.updateInput();
12836 var reader = new FileReader();
12837 reader.addEventListener("load", function() {
12838 data.srcdata = reader.result;
12841 reader.readAsDataURL(data.srcfile);
12846 removeCard : function(id)
12849 var card = this.fileCollection.get(id);
12850 card.data.is_deleted = 1;
12851 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12852 //this.fileCollection.remove(card);
12853 //this.items = this.items.filter(function(e) { return e != card });
12854 // dont' really need ot update items.
12855 card.el.dom.parentNode.removeChild(card.el.dom);
12856 this.updateInput();
12862 this.fileCollection.each(function(card) {
12863 if (card.el.dom && card.el.dom.parentNode) {
12864 card.el.dom.parentNode.removeChild(card.el.dom);
12867 this.fileCollection.clear();
12868 this.updateInput();
12871 updateInput : function()
12874 this.fileCollection.each(function(e) {
12878 this.inputEl().dom.value = JSON.stringify(data);
12888 Roo.bootstrap.CardUploader.ID = -1;/*
12890 * Ext JS Library 1.1.1
12891 * Copyright(c) 2006-2007, Ext JS, LLC.
12893 * Originally Released Under LGPL - original licence link has changed is not relivant.
12896 * <script type="text/javascript">
12901 * @class Roo.data.SortTypes
12903 * Defines the default sorting (casting?) comparison functions used when sorting data.
12905 Roo.data.SortTypes = {
12907 * Default sort that does nothing
12908 * @param {Mixed} s The value being converted
12909 * @return {Mixed} The comparison value
12911 none : function(s){
12916 * The regular expression used to strip tags
12920 stripTagsRE : /<\/?[^>]+>/gi,
12923 * Strips all HTML tags to sort on text only
12924 * @param {Mixed} s The value being converted
12925 * @return {String} The comparison value
12927 asText : function(s){
12928 return String(s).replace(this.stripTagsRE, "");
12932 * Strips all HTML tags to sort on text only - Case insensitive
12933 * @param {Mixed} s The value being converted
12934 * @return {String} The comparison value
12936 asUCText : function(s){
12937 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12941 * Case insensitive string
12942 * @param {Mixed} s The value being converted
12943 * @return {String} The comparison value
12945 asUCString : function(s) {
12946 return String(s).toUpperCase();
12951 * @param {Mixed} s The value being converted
12952 * @return {Number} The comparison value
12954 asDate : function(s) {
12958 if(s instanceof Date){
12959 return s.getTime();
12961 return Date.parse(String(s));
12966 * @param {Mixed} s The value being converted
12967 * @return {Float} The comparison value
12969 asFloat : function(s) {
12970 var val = parseFloat(String(s).replace(/,/g, ""));
12979 * @param {Mixed} s The value being converted
12980 * @return {Number} The comparison value
12982 asInt : function(s) {
12983 var val = parseInt(String(s).replace(/,/g, ""));
12991 * Ext JS Library 1.1.1
12992 * Copyright(c) 2006-2007, Ext JS, LLC.
12994 * Originally Released Under LGPL - original licence link has changed is not relivant.
12997 * <script type="text/javascript">
13001 * @class Roo.data.Record
13002 * Instances of this class encapsulate both record <em>definition</em> information, and record
13003 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13004 * to access Records cached in an {@link Roo.data.Store} object.<br>
13006 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13007 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13010 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13012 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13013 * {@link #create}. The parameters are the same.
13014 * @param {Array} data An associative Array of data values keyed by the field name.
13015 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13016 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13017 * not specified an integer id is generated.
13019 Roo.data.Record = function(data, id){
13020 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13025 * Generate a constructor for a specific record layout.
13026 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13027 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13028 * Each field definition object may contain the following properties: <ul>
13029 * <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,
13030 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13031 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13032 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13033 * is being used, then this is a string containing the javascript expression to reference the data relative to
13034 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13035 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13036 * this may be omitted.</p></li>
13037 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13038 * <ul><li>auto (Default, implies no conversion)</li>
13043 * <li>date</li></ul></p></li>
13044 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13045 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13046 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13047 * by the Reader into an object that will be stored in the Record. It is passed the
13048 * following parameters:<ul>
13049 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13051 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13053 * <br>usage:<br><pre><code>
13054 var TopicRecord = Roo.data.Record.create(
13055 {name: 'title', mapping: 'topic_title'},
13056 {name: 'author', mapping: 'username'},
13057 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13058 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13059 {name: 'lastPoster', mapping: 'user2'},
13060 {name: 'excerpt', mapping: 'post_text'}
13063 var myNewRecord = new TopicRecord({
13064 title: 'Do my job please',
13067 lastPost: new Date(),
13068 lastPoster: 'Animal',
13069 excerpt: 'No way dude!'
13071 myStore.add(myNewRecord);
13076 Roo.data.Record.create = function(o){
13077 var f = function(){
13078 f.superclass.constructor.apply(this, arguments);
13080 Roo.extend(f, Roo.data.Record);
13081 var p = f.prototype;
13082 p.fields = new Roo.util.MixedCollection(false, function(field){
13085 for(var i = 0, len = o.length; i < len; i++){
13086 p.fields.add(new Roo.data.Field(o[i]));
13088 f.getField = function(name){
13089 return p.fields.get(name);
13094 Roo.data.Record.AUTO_ID = 1000;
13095 Roo.data.Record.EDIT = 'edit';
13096 Roo.data.Record.REJECT = 'reject';
13097 Roo.data.Record.COMMIT = 'commit';
13099 Roo.data.Record.prototype = {
13101 * Readonly flag - true if this record has been modified.
13110 join : function(store){
13111 this.store = store;
13115 * Set the named field to the specified value.
13116 * @param {String} name The name of the field to set.
13117 * @param {Object} value The value to set the field to.
13119 set : function(name, value){
13120 if(this.data[name] == value){
13124 if(!this.modified){
13125 this.modified = {};
13127 if(typeof this.modified[name] == 'undefined'){
13128 this.modified[name] = this.data[name];
13130 this.data[name] = value;
13131 if(!this.editing && this.store){
13132 this.store.afterEdit(this);
13137 * Get the value of the named field.
13138 * @param {String} name The name of the field to get the value of.
13139 * @return {Object} The value of the field.
13141 get : function(name){
13142 return this.data[name];
13146 beginEdit : function(){
13147 this.editing = true;
13148 this.modified = {};
13152 cancelEdit : function(){
13153 this.editing = false;
13154 delete this.modified;
13158 endEdit : function(){
13159 this.editing = false;
13160 if(this.dirty && this.store){
13161 this.store.afterEdit(this);
13166 * Usually called by the {@link Roo.data.Store} which owns the Record.
13167 * Rejects all changes made to the Record since either creation, or the last commit operation.
13168 * Modified fields are reverted to their original values.
13170 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13171 * of reject operations.
13173 reject : function(){
13174 var m = this.modified;
13176 if(typeof m[n] != "function"){
13177 this.data[n] = m[n];
13180 this.dirty = false;
13181 delete this.modified;
13182 this.editing = false;
13184 this.store.afterReject(this);
13189 * Usually called by the {@link Roo.data.Store} which owns the Record.
13190 * Commits all changes made to the Record since either creation, or the last commit operation.
13192 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13193 * of commit operations.
13195 commit : function(){
13196 this.dirty = false;
13197 delete this.modified;
13198 this.editing = false;
13200 this.store.afterCommit(this);
13205 hasError : function(){
13206 return this.error != null;
13210 clearError : function(){
13215 * Creates a copy of this record.
13216 * @param {String} id (optional) A new record id if you don't want to use this record's id
13219 copy : function(newId) {
13220 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13224 * Ext JS Library 1.1.1
13225 * Copyright(c) 2006-2007, Ext JS, LLC.
13227 * Originally Released Under LGPL - original licence link has changed is not relivant.
13230 * <script type="text/javascript">
13236 * @class Roo.data.Store
13237 * @extends Roo.util.Observable
13238 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13239 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13241 * 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
13242 * has no knowledge of the format of the data returned by the Proxy.<br>
13244 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13245 * instances from the data object. These records are cached and made available through accessor functions.
13247 * Creates a new Store.
13248 * @param {Object} config A config object containing the objects needed for the Store to access data,
13249 * and read the data into Records.
13251 Roo.data.Store = function(config){
13252 this.data = new Roo.util.MixedCollection(false);
13253 this.data.getKey = function(o){
13256 this.baseParams = {};
13258 this.paramNames = {
13263 "multisort" : "_multisort"
13266 if(config && config.data){
13267 this.inlineData = config.data;
13268 delete config.data;
13271 Roo.apply(this, config);
13273 if(this.reader){ // reader passed
13274 this.reader = Roo.factory(this.reader, Roo.data);
13275 this.reader.xmodule = this.xmodule || false;
13276 if(!this.recordType){
13277 this.recordType = this.reader.recordType;
13279 if(this.reader.onMetaChange){
13280 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13284 if(this.recordType){
13285 this.fields = this.recordType.prototype.fields;
13287 this.modified = [];
13291 * @event datachanged
13292 * Fires when the data cache has changed, and a widget which is using this Store
13293 * as a Record cache should refresh its view.
13294 * @param {Store} this
13296 datachanged : true,
13298 * @event metachange
13299 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13300 * @param {Store} this
13301 * @param {Object} meta The JSON metadata
13306 * Fires when Records have been added to the Store
13307 * @param {Store} this
13308 * @param {Roo.data.Record[]} records The array of Records added
13309 * @param {Number} index The index at which the record(s) were added
13314 * Fires when a Record has been removed from the Store
13315 * @param {Store} this
13316 * @param {Roo.data.Record} record The Record that was removed
13317 * @param {Number} index The index at which the record was removed
13322 * Fires when a Record has been updated
13323 * @param {Store} this
13324 * @param {Roo.data.Record} record The Record that was updated
13325 * @param {String} operation The update operation being performed. Value may be one of:
13327 Roo.data.Record.EDIT
13328 Roo.data.Record.REJECT
13329 Roo.data.Record.COMMIT
13335 * Fires when the data cache has been cleared.
13336 * @param {Store} this
13340 * @event beforeload
13341 * Fires before a request is made for a new data object. If the beforeload handler returns false
13342 * the load action will be canceled.
13343 * @param {Store} this
13344 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13348 * @event beforeloadadd
13349 * Fires after a new set of Records has been loaded.
13350 * @param {Store} this
13351 * @param {Roo.data.Record[]} records The Records that were loaded
13352 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13354 beforeloadadd : true,
13357 * Fires after a new set of Records has been loaded, before they are added to the store.
13358 * @param {Store} this
13359 * @param {Roo.data.Record[]} records The Records that were loaded
13360 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13361 * @params {Object} return from reader
13365 * @event loadexception
13366 * Fires if an exception occurs in the Proxy during loading.
13367 * Called with the signature of the Proxy's "loadexception" event.
13368 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13371 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13372 * @param {Object} load options
13373 * @param {Object} jsonData from your request (normally this contains the Exception)
13375 loadexception : true
13379 this.proxy = Roo.factory(this.proxy, Roo.data);
13380 this.proxy.xmodule = this.xmodule || false;
13381 this.relayEvents(this.proxy, ["loadexception"]);
13383 this.sortToggle = {};
13384 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13386 Roo.data.Store.superclass.constructor.call(this);
13388 if(this.inlineData){
13389 this.loadData(this.inlineData);
13390 delete this.inlineData;
13394 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13396 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13397 * without a remote query - used by combo/forms at present.
13401 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13404 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13407 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13408 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13411 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13412 * on any HTTP request
13415 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13418 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13422 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13423 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13425 remoteSort : false,
13428 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13429 * loaded or when a record is removed. (defaults to false).
13431 pruneModifiedRecords : false,
13434 lastOptions : null,
13437 * Add Records to the Store and fires the add event.
13438 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13440 add : function(records){
13441 records = [].concat(records);
13442 for(var i = 0, len = records.length; i < len; i++){
13443 records[i].join(this);
13445 var index = this.data.length;
13446 this.data.addAll(records);
13447 this.fireEvent("add", this, records, index);
13451 * Remove a Record from the Store and fires the remove event.
13452 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13454 remove : function(record){
13455 var index = this.data.indexOf(record);
13456 this.data.removeAt(index);
13458 if(this.pruneModifiedRecords){
13459 this.modified.remove(record);
13461 this.fireEvent("remove", this, record, index);
13465 * Remove all Records from the Store and fires the clear event.
13467 removeAll : function(){
13469 if(this.pruneModifiedRecords){
13470 this.modified = [];
13472 this.fireEvent("clear", this);
13476 * Inserts Records to the Store at the given index and fires the add event.
13477 * @param {Number} index The start index at which to insert the passed Records.
13478 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13480 insert : function(index, records){
13481 records = [].concat(records);
13482 for(var i = 0, len = records.length; i < len; i++){
13483 this.data.insert(index, records[i]);
13484 records[i].join(this);
13486 this.fireEvent("add", this, records, index);
13490 * Get the index within the cache of the passed Record.
13491 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13492 * @return {Number} The index of the passed Record. Returns -1 if not found.
13494 indexOf : function(record){
13495 return this.data.indexOf(record);
13499 * Get the index within the cache of the Record with the passed id.
13500 * @param {String} id The id of the Record to find.
13501 * @return {Number} The index of the Record. Returns -1 if not found.
13503 indexOfId : function(id){
13504 return this.data.indexOfKey(id);
13508 * Get the Record with the specified id.
13509 * @param {String} id The id of the Record to find.
13510 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13512 getById : function(id){
13513 return this.data.key(id);
13517 * Get the Record at the specified index.
13518 * @param {Number} index The index of the Record to find.
13519 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13521 getAt : function(index){
13522 return this.data.itemAt(index);
13526 * Returns a range of Records between specified indices.
13527 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13528 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13529 * @return {Roo.data.Record[]} An array of Records
13531 getRange : function(start, end){
13532 return this.data.getRange(start, end);
13536 storeOptions : function(o){
13537 o = Roo.apply({}, o);
13540 this.lastOptions = o;
13544 * Loads the Record cache from the configured Proxy using the configured Reader.
13546 * If using remote paging, then the first load call must specify the <em>start</em>
13547 * and <em>limit</em> properties in the options.params property to establish the initial
13548 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13550 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13551 * and this call will return before the new data has been loaded. Perform any post-processing
13552 * in a callback function, or in a "load" event handler.</strong>
13554 * @param {Object} options An object containing properties which control loading options:<ul>
13555 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13556 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13557 * passed the following arguments:<ul>
13558 * <li>r : Roo.data.Record[]</li>
13559 * <li>options: Options object from the load call</li>
13560 * <li>success: Boolean success indicator</li></ul></li>
13561 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13562 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13565 load : function(options){
13566 options = options || {};
13567 if(this.fireEvent("beforeload", this, options) !== false){
13568 this.storeOptions(options);
13569 var p = Roo.apply(options.params || {}, this.baseParams);
13570 // if meta was not loaded from remote source.. try requesting it.
13571 if (!this.reader.metaFromRemote) {
13572 p._requestMeta = 1;
13574 if(this.sortInfo && this.remoteSort){
13575 var pn = this.paramNames;
13576 p[pn["sort"]] = this.sortInfo.field;
13577 p[pn["dir"]] = this.sortInfo.direction;
13579 if (this.multiSort) {
13580 var pn = this.paramNames;
13581 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13584 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13589 * Reloads the Record cache from the configured Proxy using the configured Reader and
13590 * the options from the last load operation performed.
13591 * @param {Object} options (optional) An object containing properties which may override the options
13592 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13593 * the most recently used options are reused).
13595 reload : function(options){
13596 this.load(Roo.applyIf(options||{}, this.lastOptions));
13600 // Called as a callback by the Reader during a load operation.
13601 loadRecords : function(o, options, success){
13602 if(!o || success === false){
13603 if(success !== false){
13604 this.fireEvent("load", this, [], options, o);
13606 if(options.callback){
13607 options.callback.call(options.scope || this, [], options, false);
13611 // if data returned failure - throw an exception.
13612 if (o.success === false) {
13613 // show a message if no listener is registered.
13614 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13615 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13617 // loadmask wil be hooked into this..
13618 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13621 var r = o.records, t = o.totalRecords || r.length;
13623 this.fireEvent("beforeloadadd", this, r, options, o);
13625 if(!options || options.add !== true){
13626 if(this.pruneModifiedRecords){
13627 this.modified = [];
13629 for(var i = 0, len = r.length; i < len; i++){
13633 this.data = this.snapshot;
13634 delete this.snapshot;
13637 this.data.addAll(r);
13638 this.totalLength = t;
13640 this.fireEvent("datachanged", this);
13642 this.totalLength = Math.max(t, this.data.length+r.length);
13646 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13648 var e = new Roo.data.Record({});
13650 e.set(this.parent.displayField, this.parent.emptyTitle);
13651 e.set(this.parent.valueField, '');
13656 this.fireEvent("load", this, r, options, o);
13657 if(options.callback){
13658 options.callback.call(options.scope || this, r, options, true);
13664 * Loads data from a passed data block. A Reader which understands the format of the data
13665 * must have been configured in the constructor.
13666 * @param {Object} data The data block from which to read the Records. The format of the data expected
13667 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13668 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13670 loadData : function(o, append){
13671 var r = this.reader.readRecords(o);
13672 this.loadRecords(r, {add: append}, true);
13676 * using 'cn' the nested child reader read the child array into it's child stores.
13677 * @param {Object} rec The record with a 'children array
13679 loadDataFromChildren : function(rec)
13681 this.loadData(this.reader.toLoadData(rec));
13686 * Gets the number of cached records.
13688 * <em>If using paging, this may not be the total size of the dataset. If the data object
13689 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13690 * the data set size</em>
13692 getCount : function(){
13693 return this.data.length || 0;
13697 * Gets the total number of records in the dataset as returned by the server.
13699 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13700 * the dataset size</em>
13702 getTotalCount : function(){
13703 return this.totalLength || 0;
13707 * Returns the sort state of the Store as an object with two properties:
13709 field {String} The name of the field by which the Records are sorted
13710 direction {String} The sort order, "ASC" or "DESC"
13713 getSortState : function(){
13714 return this.sortInfo;
13718 applySort : function(){
13719 if(this.sortInfo && !this.remoteSort){
13720 var s = this.sortInfo, f = s.field;
13721 var st = this.fields.get(f).sortType;
13722 var fn = function(r1, r2){
13723 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13724 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13726 this.data.sort(s.direction, fn);
13727 if(this.snapshot && this.snapshot != this.data){
13728 this.snapshot.sort(s.direction, fn);
13734 * Sets the default sort column and order to be used by the next load operation.
13735 * @param {String} fieldName The name of the field to sort by.
13736 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13738 setDefaultSort : function(field, dir){
13739 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13743 * Sort the Records.
13744 * If remote sorting is used, the sort is performed on the server, and the cache is
13745 * reloaded. If local sorting is used, the cache is sorted internally.
13746 * @param {String} fieldName The name of the field to sort by.
13747 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13749 sort : function(fieldName, dir){
13750 var f = this.fields.get(fieldName);
13752 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13754 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13755 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13760 this.sortToggle[f.name] = dir;
13761 this.sortInfo = {field: f.name, direction: dir};
13762 if(!this.remoteSort){
13764 this.fireEvent("datachanged", this);
13766 this.load(this.lastOptions);
13771 * Calls the specified function for each of the Records in the cache.
13772 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13773 * Returning <em>false</em> aborts and exits the iteration.
13774 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13776 each : function(fn, scope){
13777 this.data.each(fn, scope);
13781 * Gets all records modified since the last commit. Modified records are persisted across load operations
13782 * (e.g., during paging).
13783 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13785 getModifiedRecords : function(){
13786 return this.modified;
13790 createFilterFn : function(property, value, anyMatch){
13791 if(!value.exec){ // not a regex
13792 value = String(value);
13793 if(value.length == 0){
13796 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13798 return function(r){
13799 return value.test(r.data[property]);
13804 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13805 * @param {String} property A field on your records
13806 * @param {Number} start The record index to start at (defaults to 0)
13807 * @param {Number} end The last record index to include (defaults to length - 1)
13808 * @return {Number} The sum
13810 sum : function(property, start, end){
13811 var rs = this.data.items, v = 0;
13812 start = start || 0;
13813 end = (end || end === 0) ? end : rs.length-1;
13815 for(var i = start; i <= end; i++){
13816 v += (rs[i].data[property] || 0);
13822 * Filter the records by a specified property.
13823 * @param {String} field A field on your records
13824 * @param {String/RegExp} value Either a string that the field
13825 * should start with or a RegExp to test against the field
13826 * @param {Boolean} anyMatch True to match any part not just the beginning
13828 filter : function(property, value, anyMatch){
13829 var fn = this.createFilterFn(property, value, anyMatch);
13830 return fn ? this.filterBy(fn) : this.clearFilter();
13834 * Filter by a function. The specified function will be called with each
13835 * record in this data source. If the function returns true the record is included,
13836 * otherwise it is filtered.
13837 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13838 * @param {Object} scope (optional) The scope of the function (defaults to this)
13840 filterBy : function(fn, scope){
13841 this.snapshot = this.snapshot || this.data;
13842 this.data = this.queryBy(fn, scope||this);
13843 this.fireEvent("datachanged", this);
13847 * Query the records by a specified property.
13848 * @param {String} field A field on your records
13849 * @param {String/RegExp} value Either a string that the field
13850 * should start with or a RegExp to test against the field
13851 * @param {Boolean} anyMatch True to match any part not just the beginning
13852 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13854 query : function(property, value, anyMatch){
13855 var fn = this.createFilterFn(property, value, anyMatch);
13856 return fn ? this.queryBy(fn) : this.data.clone();
13860 * Query by a function. The specified function will be called with each
13861 * record in this data source. If the function returns true the record is included
13863 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13864 * @param {Object} scope (optional) The scope of the function (defaults to this)
13865 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13867 queryBy : function(fn, scope){
13868 var data = this.snapshot || this.data;
13869 return data.filterBy(fn, scope||this);
13873 * Collects unique values for a particular dataIndex from this store.
13874 * @param {String} dataIndex The property to collect
13875 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13876 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13877 * @return {Array} An array of the unique values
13879 collect : function(dataIndex, allowNull, bypassFilter){
13880 var d = (bypassFilter === true && this.snapshot) ?
13881 this.snapshot.items : this.data.items;
13882 var v, sv, r = [], l = {};
13883 for(var i = 0, len = d.length; i < len; i++){
13884 v = d[i].data[dataIndex];
13886 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13895 * Revert to a view of the Record cache with no filtering applied.
13896 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13898 clearFilter : function(suppressEvent){
13899 if(this.snapshot && this.snapshot != this.data){
13900 this.data = this.snapshot;
13901 delete this.snapshot;
13902 if(suppressEvent !== true){
13903 this.fireEvent("datachanged", this);
13909 afterEdit : function(record){
13910 if(this.modified.indexOf(record) == -1){
13911 this.modified.push(record);
13913 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13917 afterReject : function(record){
13918 this.modified.remove(record);
13919 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13923 afterCommit : function(record){
13924 this.modified.remove(record);
13925 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13929 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13930 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13932 commitChanges : function(){
13933 var m = this.modified.slice(0);
13934 this.modified = [];
13935 for(var i = 0, len = m.length; i < len; i++){
13941 * Cancel outstanding changes on all changed records.
13943 rejectChanges : function(){
13944 var m = this.modified.slice(0);
13945 this.modified = [];
13946 for(var i = 0, len = m.length; i < len; i++){
13951 onMetaChange : function(meta, rtype, o){
13952 this.recordType = rtype;
13953 this.fields = rtype.prototype.fields;
13954 delete this.snapshot;
13955 this.sortInfo = meta.sortInfo || this.sortInfo;
13956 this.modified = [];
13957 this.fireEvent('metachange', this, this.reader.meta);
13960 moveIndex : function(data, type)
13962 var index = this.indexOf(data);
13964 var newIndex = index + type;
13968 this.insert(newIndex, data);
13973 * Ext JS Library 1.1.1
13974 * Copyright(c) 2006-2007, Ext JS, LLC.
13976 * Originally Released Under LGPL - original licence link has changed is not relivant.
13979 * <script type="text/javascript">
13983 * @class Roo.data.SimpleStore
13984 * @extends Roo.data.Store
13985 * Small helper class to make creating Stores from Array data easier.
13986 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13987 * @cfg {Array} fields An array of field definition objects, or field name strings.
13988 * @cfg {Object} an existing reader (eg. copied from another store)
13989 * @cfg {Array} data The multi-dimensional array of data
13991 * @param {Object} config
13993 Roo.data.SimpleStore = function(config)
13995 Roo.data.SimpleStore.superclass.constructor.call(this, {
13997 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14000 Roo.data.Record.create(config.fields)
14002 proxy : new Roo.data.MemoryProxy(config.data)
14006 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14008 * Ext JS Library 1.1.1
14009 * Copyright(c) 2006-2007, Ext JS, LLC.
14011 * Originally Released Under LGPL - original licence link has changed is not relivant.
14014 * <script type="text/javascript">
14019 * @extends Roo.data.Store
14020 * @class Roo.data.JsonStore
14021 * Small helper class to make creating Stores for JSON data easier. <br/>
14023 var store = new Roo.data.JsonStore({
14024 url: 'get-images.php',
14026 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14029 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14030 * JsonReader and HttpProxy (unless inline data is provided).</b>
14031 * @cfg {Array} fields An array of field definition objects, or field name strings.
14033 * @param {Object} config
14035 Roo.data.JsonStore = function(c){
14036 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14037 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14038 reader: new Roo.data.JsonReader(c, c.fields)
14041 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14043 * Ext JS Library 1.1.1
14044 * Copyright(c) 2006-2007, Ext JS, LLC.
14046 * Originally Released Under LGPL - original licence link has changed is not relivant.
14049 * <script type="text/javascript">
14053 Roo.data.Field = function(config){
14054 if(typeof config == "string"){
14055 config = {name: config};
14057 Roo.apply(this, config);
14060 this.type = "auto";
14063 var st = Roo.data.SortTypes;
14064 // named sortTypes are supported, here we look them up
14065 if(typeof this.sortType == "string"){
14066 this.sortType = st[this.sortType];
14069 // set default sortType for strings and dates
14070 if(!this.sortType){
14073 this.sortType = st.asUCString;
14076 this.sortType = st.asDate;
14079 this.sortType = st.none;
14084 var stripRe = /[\$,%]/g;
14086 // prebuilt conversion function for this field, instead of
14087 // switching every time we're reading a value
14089 var cv, dateFormat = this.dateFormat;
14094 cv = function(v){ return v; };
14097 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14101 return v !== undefined && v !== null && v !== '' ?
14102 parseInt(String(v).replace(stripRe, ""), 10) : '';
14107 return v !== undefined && v !== null && v !== '' ?
14108 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14113 cv = function(v){ return v === true || v === "true" || v == 1; };
14120 if(v instanceof Date){
14124 if(dateFormat == "timestamp"){
14125 return new Date(v*1000);
14127 return Date.parseDate(v, dateFormat);
14129 var parsed = Date.parse(v);
14130 return parsed ? new Date(parsed) : null;
14139 Roo.data.Field.prototype = {
14147 * Ext JS Library 1.1.1
14148 * Copyright(c) 2006-2007, Ext JS, LLC.
14150 * Originally Released Under LGPL - original licence link has changed is not relivant.
14153 * <script type="text/javascript">
14156 // Base class for reading structured data from a data source. This class is intended to be
14157 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14160 * @class Roo.data.DataReader
14161 * Base class for reading structured data from a data source. This class is intended to be
14162 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14165 Roo.data.DataReader = function(meta, recordType){
14169 this.recordType = recordType instanceof Array ?
14170 Roo.data.Record.create(recordType) : recordType;
14173 Roo.data.DataReader.prototype = {
14176 readerType : 'Data',
14178 * Create an empty record
14179 * @param {Object} data (optional) - overlay some values
14180 * @return {Roo.data.Record} record created.
14182 newRow : function(d) {
14184 this.recordType.prototype.fields.each(function(c) {
14186 case 'int' : da[c.name] = 0; break;
14187 case 'date' : da[c.name] = new Date(); break;
14188 case 'float' : da[c.name] = 0.0; break;
14189 case 'boolean' : da[c.name] = false; break;
14190 default : da[c.name] = ""; break;
14194 return new this.recordType(Roo.apply(da, d));
14200 * Ext JS Library 1.1.1
14201 * Copyright(c) 2006-2007, Ext JS, LLC.
14203 * Originally Released Under LGPL - original licence link has changed is not relivant.
14206 * <script type="text/javascript">
14210 * @class Roo.data.DataProxy
14211 * @extends Roo.data.Observable
14212 * This class is an abstract base class for implementations which provide retrieval of
14213 * unformatted data objects.<br>
14215 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14216 * (of the appropriate type which knows how to parse the data object) to provide a block of
14217 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14219 * Custom implementations must implement the load method as described in
14220 * {@link Roo.data.HttpProxy#load}.
14222 Roo.data.DataProxy = function(){
14225 * @event beforeload
14226 * Fires before a network request is made to retrieve a data object.
14227 * @param {Object} This DataProxy object.
14228 * @param {Object} params The params parameter to the load function.
14233 * Fires before the load method's callback is called.
14234 * @param {Object} This DataProxy object.
14235 * @param {Object} o The data object.
14236 * @param {Object} arg The callback argument object passed to the load function.
14240 * @event loadexception
14241 * Fires if an Exception occurs during data retrieval.
14242 * @param {Object} This DataProxy object.
14243 * @param {Object} o The data object.
14244 * @param {Object} arg The callback argument object passed to the load function.
14245 * @param {Object} e The Exception.
14247 loadexception : true
14249 Roo.data.DataProxy.superclass.constructor.call(this);
14252 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14255 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14259 * Ext JS Library 1.1.1
14260 * Copyright(c) 2006-2007, Ext JS, LLC.
14262 * Originally Released Under LGPL - original licence link has changed is not relivant.
14265 * <script type="text/javascript">
14268 * @class Roo.data.MemoryProxy
14269 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14270 * to the Reader when its load method is called.
14272 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14274 Roo.data.MemoryProxy = function(data){
14278 Roo.data.MemoryProxy.superclass.constructor.call(this);
14282 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14285 * Load data from the requested source (in this case an in-memory
14286 * data object passed to the constructor), read the data object into
14287 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14288 * process that block using the passed callback.
14289 * @param {Object} params This parameter is not used by the MemoryProxy class.
14290 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14291 * object into a block of Roo.data.Records.
14292 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14293 * The function must be passed <ul>
14294 * <li>The Record block object</li>
14295 * <li>The "arg" argument from the load function</li>
14296 * <li>A boolean success indicator</li>
14298 * @param {Object} scope The scope in which to call the callback
14299 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14301 load : function(params, reader, callback, scope, arg){
14302 params = params || {};
14305 result = reader.readRecords(params.data ? params.data :this.data);
14307 this.fireEvent("loadexception", this, arg, null, e);
14308 callback.call(scope, null, arg, false);
14311 callback.call(scope, result, arg, true);
14315 update : function(params, records){
14320 * Ext JS Library 1.1.1
14321 * Copyright(c) 2006-2007, Ext JS, LLC.
14323 * Originally Released Under LGPL - original licence link has changed is not relivant.
14326 * <script type="text/javascript">
14329 * @class Roo.data.HttpProxy
14330 * @extends Roo.data.DataProxy
14331 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14332 * configured to reference a certain URL.<br><br>
14334 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14335 * from which the running page was served.<br><br>
14337 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14339 * Be aware that to enable the browser to parse an XML document, the server must set
14340 * the Content-Type header in the HTTP response to "text/xml".
14342 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14343 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14344 * will be used to make the request.
14346 Roo.data.HttpProxy = function(conn){
14347 Roo.data.HttpProxy.superclass.constructor.call(this);
14348 // is conn a conn config or a real conn?
14350 this.useAjax = !conn || !conn.events;
14354 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14355 // thse are take from connection...
14358 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14361 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14362 * extra parameters to each request made by this object. (defaults to undefined)
14365 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14366 * to each request made by this object. (defaults to undefined)
14369 * @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)
14372 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14375 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14381 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14385 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14386 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14387 * a finer-grained basis than the DataProxy events.
14389 getConnection : function(){
14390 return this.useAjax ? Roo.Ajax : this.conn;
14394 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14395 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14396 * process that block using the passed callback.
14397 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14398 * for the request to the remote server.
14399 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14400 * object into a block of Roo.data.Records.
14401 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14402 * The function must be passed <ul>
14403 * <li>The Record block object</li>
14404 * <li>The "arg" argument from the load function</li>
14405 * <li>A boolean success indicator</li>
14407 * @param {Object} scope The scope in which to call the callback
14408 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14410 load : function(params, reader, callback, scope, arg){
14411 if(this.fireEvent("beforeload", this, params) !== false){
14413 params : params || {},
14415 callback : callback,
14420 callback : this.loadResponse,
14424 Roo.applyIf(o, this.conn);
14425 if(this.activeRequest){
14426 Roo.Ajax.abort(this.activeRequest);
14428 this.activeRequest = Roo.Ajax.request(o);
14430 this.conn.request(o);
14433 callback.call(scope||this, null, arg, false);
14438 loadResponse : function(o, success, response){
14439 delete this.activeRequest;
14441 this.fireEvent("loadexception", this, o, response);
14442 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14447 result = o.reader.read(response);
14449 this.fireEvent("loadexception", this, o, response, e);
14450 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14454 this.fireEvent("load", this, o, o.request.arg);
14455 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14459 update : function(dataSet){
14464 updateResponse : function(dataSet){
14469 * Ext JS Library 1.1.1
14470 * Copyright(c) 2006-2007, Ext JS, LLC.
14472 * Originally Released Under LGPL - original licence link has changed is not relivant.
14475 * <script type="text/javascript">
14479 * @class Roo.data.ScriptTagProxy
14480 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14481 * other than the originating domain of the running page.<br><br>
14483 * <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
14484 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14486 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14487 * source code that is used as the source inside a <script> tag.<br><br>
14489 * In order for the browser to process the returned data, the server must wrap the data object
14490 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14491 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14492 * depending on whether the callback name was passed:
14495 boolean scriptTag = false;
14496 String cb = request.getParameter("callback");
14499 response.setContentType("text/javascript");
14501 response.setContentType("application/x-json");
14503 Writer out = response.getWriter();
14505 out.write(cb + "(");
14507 out.print(dataBlock.toJsonString());
14514 * @param {Object} config A configuration object.
14516 Roo.data.ScriptTagProxy = function(config){
14517 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14518 Roo.apply(this, config);
14519 this.head = document.getElementsByTagName("head")[0];
14522 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14524 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14526 * @cfg {String} url The URL from which to request the data object.
14529 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14533 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14534 * the server the name of the callback function set up by the load call to process the returned data object.
14535 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14536 * javascript output which calls this named function passing the data object as its only parameter.
14538 callbackParam : "callback",
14540 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14541 * name to the request.
14546 * Load data from the configured URL, read the data object into
14547 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14548 * process that block using the passed callback.
14549 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14550 * for the request to the remote server.
14551 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14552 * object into a block of Roo.data.Records.
14553 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14554 * The function must be passed <ul>
14555 * <li>The Record block object</li>
14556 * <li>The "arg" argument from the load function</li>
14557 * <li>A boolean success indicator</li>
14559 * @param {Object} scope The scope in which to call the callback
14560 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14562 load : function(params, reader, callback, scope, arg){
14563 if(this.fireEvent("beforeload", this, params) !== false){
14565 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14567 var url = this.url;
14568 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14570 url += "&_dc=" + (new Date().getTime());
14572 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14575 cb : "stcCallback"+transId,
14576 scriptId : "stcScript"+transId,
14580 callback : callback,
14586 window[trans.cb] = function(o){
14587 conn.handleResponse(o, trans);
14590 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14592 if(this.autoAbort !== false){
14596 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14598 var script = document.createElement("script");
14599 script.setAttribute("src", url);
14600 script.setAttribute("type", "text/javascript");
14601 script.setAttribute("id", trans.scriptId);
14602 this.head.appendChild(script);
14604 this.trans = trans;
14606 callback.call(scope||this, null, arg, false);
14611 isLoading : function(){
14612 return this.trans ? true : false;
14616 * Abort the current server request.
14618 abort : function(){
14619 if(this.isLoading()){
14620 this.destroyTrans(this.trans);
14625 destroyTrans : function(trans, isLoaded){
14626 this.head.removeChild(document.getElementById(trans.scriptId));
14627 clearTimeout(trans.timeoutId);
14629 window[trans.cb] = undefined;
14631 delete window[trans.cb];
14634 // if hasn't been loaded, wait for load to remove it to prevent script error
14635 window[trans.cb] = function(){
14636 window[trans.cb] = undefined;
14638 delete window[trans.cb];
14645 handleResponse : function(o, trans){
14646 this.trans = false;
14647 this.destroyTrans(trans, true);
14650 result = trans.reader.readRecords(o);
14652 this.fireEvent("loadexception", this, o, trans.arg, e);
14653 trans.callback.call(trans.scope||window, null, trans.arg, false);
14656 this.fireEvent("load", this, o, trans.arg);
14657 trans.callback.call(trans.scope||window, result, trans.arg, true);
14661 handleFailure : function(trans){
14662 this.trans = false;
14663 this.destroyTrans(trans, false);
14664 this.fireEvent("loadexception", this, null, trans.arg);
14665 trans.callback.call(trans.scope||window, null, trans.arg, false);
14669 * Ext JS Library 1.1.1
14670 * Copyright(c) 2006-2007, Ext JS, LLC.
14672 * Originally Released Under LGPL - original licence link has changed is not relivant.
14675 * <script type="text/javascript">
14679 * @class Roo.data.JsonReader
14680 * @extends Roo.data.DataReader
14681 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14682 * based on mappings in a provided Roo.data.Record constructor.
14684 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14685 * in the reply previously.
14690 var RecordDef = Roo.data.Record.create([
14691 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14692 {name: 'occupation'} // This field will use "occupation" as the mapping.
14694 var myReader = new Roo.data.JsonReader({
14695 totalProperty: "results", // The property which contains the total dataset size (optional)
14696 root: "rows", // The property which contains an Array of row objects
14697 id: "id" // The property within each row object that provides an ID for the record (optional)
14701 * This would consume a JSON file like this:
14703 { 'results': 2, 'rows': [
14704 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14705 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14708 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14709 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14710 * paged from the remote server.
14711 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14712 * @cfg {String} root name of the property which contains the Array of row objects.
14713 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14714 * @cfg {Array} fields Array of field definition objects
14716 * Create a new JsonReader
14717 * @param {Object} meta Metadata configuration options
14718 * @param {Object} recordType Either an Array of field definition objects,
14719 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14721 Roo.data.JsonReader = function(meta, recordType){
14724 // set some defaults:
14725 Roo.applyIf(meta, {
14726 totalProperty: 'total',
14727 successProperty : 'success',
14732 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14734 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14736 readerType : 'Json',
14739 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14740 * Used by Store query builder to append _requestMeta to params.
14743 metaFromRemote : false,
14745 * This method is only used by a DataProxy which has retrieved data from a remote server.
14746 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14747 * @return {Object} data A data block which is used by an Roo.data.Store object as
14748 * a cache of Roo.data.Records.
14750 read : function(response){
14751 var json = response.responseText;
14753 var o = /* eval:var:o */ eval("("+json+")");
14755 throw {message: "JsonReader.read: Json object not found"};
14761 this.metaFromRemote = true;
14762 this.meta = o.metaData;
14763 this.recordType = Roo.data.Record.create(o.metaData.fields);
14764 this.onMetaChange(this.meta, this.recordType, o);
14766 return this.readRecords(o);
14769 // private function a store will implement
14770 onMetaChange : function(meta, recordType, o){
14777 simpleAccess: function(obj, subsc) {
14784 getJsonAccessor: function(){
14786 return function(expr) {
14788 return(re.test(expr))
14789 ? new Function("obj", "return obj." + expr)
14794 return Roo.emptyFn;
14799 * Create a data block containing Roo.data.Records from an XML document.
14800 * @param {Object} o An object which contains an Array of row objects in the property specified
14801 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14802 * which contains the total size of the dataset.
14803 * @return {Object} data A data block which is used by an Roo.data.Store object as
14804 * a cache of Roo.data.Records.
14806 readRecords : function(o){
14808 * After any data loads, the raw JSON data is available for further custom processing.
14812 var s = this.meta, Record = this.recordType,
14813 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14815 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14817 if(s.totalProperty) {
14818 this.getTotal = this.getJsonAccessor(s.totalProperty);
14820 if(s.successProperty) {
14821 this.getSuccess = this.getJsonAccessor(s.successProperty);
14823 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14825 var g = this.getJsonAccessor(s.id);
14826 this.getId = function(rec) {
14828 return (r === undefined || r === "") ? null : r;
14831 this.getId = function(){return null;};
14834 for(var jj = 0; jj < fl; jj++){
14836 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14837 this.ef[jj] = this.getJsonAccessor(map);
14841 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14842 if(s.totalProperty){
14843 var vt = parseInt(this.getTotal(o), 10);
14848 if(s.successProperty){
14849 var vs = this.getSuccess(o);
14850 if(vs === false || vs === 'false'){
14855 for(var i = 0; i < c; i++){
14858 var id = this.getId(n);
14859 for(var j = 0; j < fl; j++){
14861 var v = this.ef[j](n);
14863 Roo.log('missing convert for ' + f.name);
14867 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14869 var record = new Record(values, id);
14871 records[i] = record;
14877 totalRecords : totalRecords
14880 // used when loading children.. @see loadDataFromChildren
14881 toLoadData: function(rec)
14883 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14884 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14885 return { data : data, total : data.length };
14890 * Ext JS Library 1.1.1
14891 * Copyright(c) 2006-2007, Ext JS, LLC.
14893 * Originally Released Under LGPL - original licence link has changed is not relivant.
14896 * <script type="text/javascript">
14900 * @class Roo.data.ArrayReader
14901 * @extends Roo.data.DataReader
14902 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14903 * Each element of that Array represents a row of data fields. The
14904 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14905 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14909 var RecordDef = Roo.data.Record.create([
14910 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14911 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14913 var myReader = new Roo.data.ArrayReader({
14914 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14918 * This would consume an Array like this:
14920 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14924 * Create a new JsonReader
14925 * @param {Object} meta Metadata configuration options.
14926 * @param {Object|Array} recordType Either an Array of field definition objects
14928 * @cfg {Array} fields Array of field definition objects
14929 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14930 * as specified to {@link Roo.data.Record#create},
14931 * or an {@link Roo.data.Record} object
14934 * created using {@link Roo.data.Record#create}.
14936 Roo.data.ArrayReader = function(meta, recordType)
14938 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14941 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14944 * Create a data block containing Roo.data.Records from an XML document.
14945 * @param {Object} o An Array of row objects which represents the dataset.
14946 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14947 * a cache of Roo.data.Records.
14949 readRecords : function(o)
14951 var sid = this.meta ? this.meta.id : null;
14952 var recordType = this.recordType, fields = recordType.prototype.fields;
14955 for(var i = 0; i < root.length; i++){
14958 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14959 for(var j = 0, jlen = fields.length; j < jlen; j++){
14960 var f = fields.items[j];
14961 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14962 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14964 values[f.name] = v;
14966 var record = new recordType(values, id);
14968 records[records.length] = record;
14972 totalRecords : records.length
14975 // used when loading children.. @see loadDataFromChildren
14976 toLoadData: function(rec)
14978 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14979 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14990 * @class Roo.bootstrap.ComboBox
14991 * @extends Roo.bootstrap.TriggerField
14992 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14993 * @cfg {Boolean} append (true|false) default false
14994 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14995 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14996 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14997 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14998 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14999 * @cfg {Boolean} animate default true
15000 * @cfg {Boolean} emptyResultText only for touch device
15001 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15002 * @cfg {String} emptyTitle default ''
15003 * @cfg {Number} width fixed with? experimental
15005 * Create a new ComboBox.
15006 * @param {Object} config Configuration options
15008 Roo.bootstrap.ComboBox = function(config){
15009 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15013 * Fires when the dropdown list is expanded
15014 * @param {Roo.bootstrap.ComboBox} combo This combo box
15019 * Fires when the dropdown list is collapsed
15020 * @param {Roo.bootstrap.ComboBox} combo This combo box
15024 * @event beforeselect
15025 * Fires before a list item is selected. Return false to cancel the selection.
15026 * @param {Roo.bootstrap.ComboBox} combo This combo box
15027 * @param {Roo.data.Record} record The data record returned from the underlying store
15028 * @param {Number} index The index of the selected item in the dropdown list
15030 'beforeselect' : true,
15033 * Fires when a list item is selected
15034 * @param {Roo.bootstrap.ComboBox} combo This combo box
15035 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15036 * @param {Number} index The index of the selected item in the dropdown list
15040 * @event beforequery
15041 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15042 * The event object passed has these properties:
15043 * @param {Roo.bootstrap.ComboBox} combo This combo box
15044 * @param {String} query The query
15045 * @param {Boolean} forceAll true to force "all" query
15046 * @param {Boolean} cancel true to cancel the query
15047 * @param {Object} e The query event object
15049 'beforequery': true,
15052 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15053 * @param {Roo.bootstrap.ComboBox} combo This combo box
15058 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15059 * @param {Roo.bootstrap.ComboBox} combo This combo box
15060 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15065 * Fires when the remove value from the combobox array
15066 * @param {Roo.bootstrap.ComboBox} combo This combo box
15070 * @event afterremove
15071 * Fires when the remove value from the combobox array
15072 * @param {Roo.bootstrap.ComboBox} combo This combo box
15074 'afterremove' : true,
15076 * @event specialfilter
15077 * Fires when specialfilter
15078 * @param {Roo.bootstrap.ComboBox} combo This combo box
15080 'specialfilter' : true,
15083 * Fires when tick the element
15084 * @param {Roo.bootstrap.ComboBox} combo This combo box
15088 * @event touchviewdisplay
15089 * Fires when touch view require special display (default is using displayField)
15090 * @param {Roo.bootstrap.ComboBox} combo This combo box
15091 * @param {Object} cfg set html .
15093 'touchviewdisplay' : true
15098 this.tickItems = [];
15100 this.selectedIndex = -1;
15101 if(this.mode == 'local'){
15102 if(config.queryDelay === undefined){
15103 this.queryDelay = 10;
15105 if(config.minChars === undefined){
15111 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15114 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15115 * rendering into an Roo.Editor, defaults to false)
15118 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15119 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15122 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15125 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15126 * the dropdown list (defaults to undefined, with no header element)
15130 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15134 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15136 listWidth: undefined,
15138 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15139 * mode = 'remote' or 'text' if mode = 'local')
15141 displayField: undefined,
15144 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15145 * mode = 'remote' or 'value' if mode = 'local').
15146 * Note: use of a valueField requires the user make a selection
15147 * in order for a value to be mapped.
15149 valueField: undefined,
15151 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15156 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15157 * field's data value (defaults to the underlying DOM element's name)
15159 hiddenName: undefined,
15161 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15165 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15167 selectedClass: 'active',
15170 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15174 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15175 * anchor positions (defaults to 'tl-bl')
15177 listAlign: 'tl-bl?',
15179 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15183 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15184 * query specified by the allQuery config option (defaults to 'query')
15186 triggerAction: 'query',
15188 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15189 * (defaults to 4, does not apply if editable = false)
15193 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15194 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15198 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15199 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15203 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15204 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15208 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15209 * when editable = true (defaults to false)
15211 selectOnFocus:false,
15213 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15215 queryParam: 'query',
15217 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15218 * when mode = 'remote' (defaults to 'Loading...')
15220 loadingText: 'Loading...',
15222 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15226 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15230 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15231 * traditional select (defaults to true)
15235 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15239 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15243 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15244 * listWidth has a higher value)
15248 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15249 * allow the user to set arbitrary text into the field (defaults to false)
15251 forceSelection:false,
15253 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15254 * if typeAhead = true (defaults to 250)
15256 typeAheadDelay : 250,
15258 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15259 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15261 valueNotFoundText : undefined,
15263 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15265 blockFocus : false,
15268 * @cfg {Boolean} disableClear Disable showing of clear button.
15270 disableClear : false,
15272 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15274 alwaysQuery : false,
15277 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15282 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15284 invalidClass : "has-warning",
15287 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15289 validClass : "has-success",
15292 * @cfg {Boolean} specialFilter (true|false) special filter default false
15294 specialFilter : false,
15297 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15299 mobileTouchView : true,
15302 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15304 useNativeIOS : false,
15307 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15309 mobile_restrict_height : false,
15311 ios_options : false,
15323 btnPosition : 'right',
15324 triggerList : true,
15325 showToggleBtn : true,
15327 emptyResultText: 'Empty',
15328 triggerText : 'Select',
15332 // element that contains real text value.. (when hidden is used..)
15334 getAutoCreate : function()
15339 * Render classic select for iso
15342 if(Roo.isIOS && this.useNativeIOS){
15343 cfg = this.getAutoCreateNativeIOS();
15351 if(Roo.isTouch && this.mobileTouchView){
15352 cfg = this.getAutoCreateTouchView();
15359 if(!this.tickable){
15360 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15365 * ComboBox with tickable selections
15368 var align = this.labelAlign || this.parentLabelAlign();
15371 cls : 'form-group roo-combobox-tickable' //input-group
15374 var btn_text_select = '';
15375 var btn_text_done = '';
15376 var btn_text_cancel = '';
15378 if (this.btn_text_show) {
15379 btn_text_select = 'Select';
15380 btn_text_done = 'Done';
15381 btn_text_cancel = 'Cancel';
15386 cls : 'tickable-buttons',
15391 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15392 //html : this.triggerText
15393 html: btn_text_select
15399 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15401 html: btn_text_done
15407 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15409 html: btn_text_cancel
15415 buttons.cn.unshift({
15417 cls: 'roo-select2-search-field-input'
15423 Roo.each(buttons.cn, function(c){
15425 c.cls += ' btn-' + _this.size;
15428 if (_this.disabled) {
15435 style : 'display: contents',
15440 cls: 'form-hidden-field'
15444 cls: 'roo-select2-choices',
15448 cls: 'roo-select2-search-field',
15459 cls: 'roo-select2-container input-group roo-select2-container-multi',
15465 // cls: 'typeahead typeahead-long dropdown-menu',
15466 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15471 if(this.hasFeedback && !this.allowBlank){
15475 cls: 'glyphicon form-control-feedback'
15478 combobox.cn.push(feedback);
15485 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15486 tooltip : 'This field is required'
15488 if (Roo.bootstrap.version == 4) {
15491 style : 'display:none'
15494 if (align ==='left' && this.fieldLabel.length) {
15496 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15503 cls : 'control-label col-form-label',
15504 html : this.fieldLabel
15516 var labelCfg = cfg.cn[1];
15517 var contentCfg = cfg.cn[2];
15520 if(this.indicatorpos == 'right'){
15526 cls : 'control-label col-form-label',
15530 html : this.fieldLabel
15546 labelCfg = cfg.cn[0];
15547 contentCfg = cfg.cn[1];
15551 if(this.labelWidth > 12){
15552 labelCfg.style = "width: " + this.labelWidth + 'px';
15554 if(this.width * 1 > 0){
15555 contentCfg.style = "width: " + this.width + 'px';
15557 if(this.labelWidth < 13 && this.labelmd == 0){
15558 this.labelmd = this.labelWidth;
15561 if(this.labellg > 0){
15562 labelCfg.cls += ' col-lg-' + this.labellg;
15563 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15566 if(this.labelmd > 0){
15567 labelCfg.cls += ' col-md-' + this.labelmd;
15568 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15571 if(this.labelsm > 0){
15572 labelCfg.cls += ' col-sm-' + this.labelsm;
15573 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15576 if(this.labelxs > 0){
15577 labelCfg.cls += ' col-xs-' + this.labelxs;
15578 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15582 } else if ( this.fieldLabel.length) {
15583 // Roo.log(" label");
15588 //cls : 'input-group-addon',
15589 html : this.fieldLabel
15594 if(this.indicatorpos == 'right'){
15598 //cls : 'input-group-addon',
15599 html : this.fieldLabel
15609 // Roo.log(" no label && no align");
15616 ['xs','sm','md','lg'].map(function(size){
15617 if (settings[size]) {
15618 cfg.cls += ' col-' + size + '-' + settings[size];
15626 _initEventsCalled : false,
15629 initEvents: function()
15631 if (this._initEventsCalled) { // as we call render... prevent looping...
15634 this._initEventsCalled = true;
15637 throw "can not find store for combo";
15640 this.indicator = this.indicatorEl();
15642 this.store = Roo.factory(this.store, Roo.data);
15643 this.store.parent = this;
15645 // if we are building from html. then this element is so complex, that we can not really
15646 // use the rendered HTML.
15647 // so we have to trash and replace the previous code.
15648 if (Roo.XComponent.build_from_html) {
15649 // remove this element....
15650 var e = this.el.dom, k=0;
15651 while (e ) { e = e.previousSibling; ++k;}
15656 this.rendered = false;
15658 this.render(this.parent().getChildContainer(true), k);
15661 if(Roo.isIOS && this.useNativeIOS){
15662 this.initIOSView();
15670 if(Roo.isTouch && this.mobileTouchView){
15671 this.initTouchView();
15676 this.initTickableEvents();
15680 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15682 if(this.hiddenName){
15684 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15686 this.hiddenField.dom.value =
15687 this.hiddenValue !== undefined ? this.hiddenValue :
15688 this.value !== undefined ? this.value : '';
15690 // prevent input submission
15691 this.el.dom.removeAttribute('name');
15692 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15697 // this.el.dom.setAttribute('autocomplete', 'off');
15700 var cls = 'x-combo-list';
15702 //this.list = new Roo.Layer({
15703 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15709 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15710 _this.list.setWidth(lw);
15713 this.list.on('mouseover', this.onViewOver, this);
15714 this.list.on('mousemove', this.onViewMove, this);
15715 this.list.on('scroll', this.onViewScroll, this);
15718 this.list.swallowEvent('mousewheel');
15719 this.assetHeight = 0;
15722 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15723 this.assetHeight += this.header.getHeight();
15726 this.innerList = this.list.createChild({cls:cls+'-inner'});
15727 this.innerList.on('mouseover', this.onViewOver, this);
15728 this.innerList.on('mousemove', this.onViewMove, this);
15729 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15731 if(this.allowBlank && !this.pageSize && !this.disableClear){
15732 this.footer = this.list.createChild({cls:cls+'-ft'});
15733 this.pageTb = new Roo.Toolbar(this.footer);
15737 this.footer = this.list.createChild({cls:cls+'-ft'});
15738 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15739 {pageSize: this.pageSize});
15743 if (this.pageTb && this.allowBlank && !this.disableClear) {
15745 this.pageTb.add(new Roo.Toolbar.Fill(), {
15746 cls: 'x-btn-icon x-btn-clear',
15748 handler: function()
15751 _this.clearValue();
15752 _this.onSelect(false, -1);
15757 this.assetHeight += this.footer.getHeight();
15762 this.tpl = Roo.bootstrap.version == 4 ?
15763 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15764 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15767 this.view = new Roo.View(this.list, this.tpl, {
15768 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15770 //this.view.wrapEl.setDisplayed(false);
15771 this.view.on('click', this.onViewClick, this);
15774 this.store.on('beforeload', this.onBeforeLoad, this);
15775 this.store.on('load', this.onLoad, this);
15776 this.store.on('loadexception', this.onLoadException, this);
15778 if(this.resizable){
15779 this.resizer = new Roo.Resizable(this.list, {
15780 pinned:true, handles:'se'
15782 this.resizer.on('resize', function(r, w, h){
15783 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15784 this.listWidth = w;
15785 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15786 this.restrictHeight();
15788 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15791 if(!this.editable){
15792 this.editable = true;
15793 this.setEditable(false);
15798 if (typeof(this.events.add.listeners) != 'undefined') {
15800 this.addicon = this.wrap.createChild(
15801 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15803 this.addicon.on('click', function(e) {
15804 this.fireEvent('add', this);
15807 if (typeof(this.events.edit.listeners) != 'undefined') {
15809 this.editicon = this.wrap.createChild(
15810 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15811 if (this.addicon) {
15812 this.editicon.setStyle('margin-left', '40px');
15814 this.editicon.on('click', function(e) {
15816 // we fire even if inothing is selected..
15817 this.fireEvent('edit', this, this.lastData );
15823 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15824 "up" : function(e){
15825 this.inKeyMode = true;
15829 "down" : function(e){
15830 if(!this.isExpanded()){
15831 this.onTriggerClick();
15833 this.inKeyMode = true;
15838 "enter" : function(e){
15839 // this.onViewClick();
15843 if(this.fireEvent("specialkey", this, e)){
15844 this.onViewClick(false);
15850 "esc" : function(e){
15854 "tab" : function(e){
15857 if(this.fireEvent("specialkey", this, e)){
15858 this.onViewClick(false);
15866 doRelay : function(foo, bar, hname){
15867 if(hname == 'down' || this.scope.isExpanded()){
15868 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15877 this.queryDelay = Math.max(this.queryDelay || 10,
15878 this.mode == 'local' ? 10 : 250);
15881 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15883 if(this.typeAhead){
15884 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15886 if(this.editable !== false){
15887 this.inputEl().on("keyup", this.onKeyUp, this);
15889 if(this.forceSelection){
15890 this.inputEl().on('blur', this.doForce, this);
15894 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15895 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15899 initTickableEvents: function()
15903 if(this.hiddenName){
15905 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15907 this.hiddenField.dom.value =
15908 this.hiddenValue !== undefined ? this.hiddenValue :
15909 this.value !== undefined ? this.value : '';
15911 // prevent input submission
15912 this.el.dom.removeAttribute('name');
15913 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15918 // this.list = this.el.select('ul.dropdown-menu',true).first();
15920 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15921 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15922 if(this.triggerList){
15923 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15926 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15927 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15929 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15930 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15932 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15933 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15935 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15936 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15937 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15940 this.cancelBtn.hide();
15945 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15946 _this.list.setWidth(lw);
15949 this.list.on('mouseover', this.onViewOver, this);
15950 this.list.on('mousemove', this.onViewMove, this);
15952 this.list.on('scroll', this.onViewScroll, this);
15955 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15956 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15959 this.view = new Roo.View(this.list, this.tpl, {
15964 selectedClass: this.selectedClass
15967 //this.view.wrapEl.setDisplayed(false);
15968 this.view.on('click', this.onViewClick, this);
15972 this.store.on('beforeload', this.onBeforeLoad, this);
15973 this.store.on('load', this.onLoad, this);
15974 this.store.on('loadexception', this.onLoadException, this);
15977 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15978 "up" : function(e){
15979 this.inKeyMode = true;
15983 "down" : function(e){
15984 this.inKeyMode = true;
15988 "enter" : function(e){
15989 if(this.fireEvent("specialkey", this, e)){
15990 this.onViewClick(false);
15996 "esc" : function(e){
15997 this.onTickableFooterButtonClick(e, false, false);
16000 "tab" : function(e){
16001 this.fireEvent("specialkey", this, e);
16003 this.onTickableFooterButtonClick(e, false, false);
16010 doRelay : function(e, fn, key){
16011 if(this.scope.isExpanded()){
16012 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16021 this.queryDelay = Math.max(this.queryDelay || 10,
16022 this.mode == 'local' ? 10 : 250);
16025 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16027 if(this.typeAhead){
16028 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16031 if(this.editable !== false){
16032 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16035 this.indicator = this.indicatorEl();
16037 if(this.indicator){
16038 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16039 this.indicator.hide();
16044 onDestroy : function(){
16046 this.view.setStore(null);
16047 this.view.el.removeAllListeners();
16048 this.view.el.remove();
16049 this.view.purgeListeners();
16052 this.list.dom.innerHTML = '';
16056 this.store.un('beforeload', this.onBeforeLoad, this);
16057 this.store.un('load', this.onLoad, this);
16058 this.store.un('loadexception', this.onLoadException, this);
16060 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16064 fireKey : function(e){
16065 if(e.isNavKeyPress() && !this.list.isVisible()){
16066 this.fireEvent("specialkey", this, e);
16071 onResize: function(w, h)
16075 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16077 // if(typeof w != 'number'){
16078 // // we do not handle it!?!?
16081 // var tw = this.trigger.getWidth();
16082 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16083 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16085 // this.inputEl().setWidth( this.adjustWidth('input', x));
16087 // //this.trigger.setStyle('left', x+'px');
16089 // if(this.list && this.listWidth === undefined){
16090 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16091 // this.list.setWidth(lw);
16092 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16100 * Allow or prevent the user from directly editing the field text. If false is passed,
16101 * the user will only be able to select from the items defined in the dropdown list. This method
16102 * is the runtime equivalent of setting the 'editable' config option at config time.
16103 * @param {Boolean} value True to allow the user to directly edit the field text
16105 setEditable : function(value){
16106 if(value == this.editable){
16109 this.editable = value;
16111 this.inputEl().dom.setAttribute('readOnly', true);
16112 this.inputEl().on('mousedown', this.onTriggerClick, this);
16113 this.inputEl().addClass('x-combo-noedit');
16115 this.inputEl().dom.setAttribute('readOnly', false);
16116 this.inputEl().un('mousedown', this.onTriggerClick, this);
16117 this.inputEl().removeClass('x-combo-noedit');
16123 onBeforeLoad : function(combo,opts){
16124 if(!this.hasFocus){
16128 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16130 this.restrictHeight();
16131 this.selectedIndex = -1;
16135 onLoad : function(){
16137 this.hasQuery = false;
16139 if(!this.hasFocus){
16143 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16144 this.loading.hide();
16147 if(this.store.getCount() > 0){
16150 this.restrictHeight();
16151 if(this.lastQuery == this.allQuery){
16152 if(this.editable && !this.tickable){
16153 this.inputEl().dom.select();
16157 !this.selectByValue(this.value, true) &&
16160 !this.store.lastOptions ||
16161 typeof(this.store.lastOptions.add) == 'undefined' ||
16162 this.store.lastOptions.add != true
16165 this.select(0, true);
16168 if(this.autoFocus){
16171 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16172 this.taTask.delay(this.typeAheadDelay);
16176 this.onEmptyResults();
16182 onLoadException : function()
16184 this.hasQuery = false;
16186 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16187 this.loading.hide();
16190 if(this.tickable && this.editable){
16195 // only causes errors at present
16196 //Roo.log(this.store.reader.jsonData);
16197 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16199 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16205 onTypeAhead : function(){
16206 if(this.store.getCount() > 0){
16207 var r = this.store.getAt(0);
16208 var newValue = r.data[this.displayField];
16209 var len = newValue.length;
16210 var selStart = this.getRawValue().length;
16212 if(selStart != len){
16213 this.setRawValue(newValue);
16214 this.selectText(selStart, newValue.length);
16220 onSelect : function(record, index){
16222 if(this.fireEvent('beforeselect', this, record, index) !== false){
16224 this.setFromData(index > -1 ? record.data : false);
16227 this.fireEvent('select', this, record, index);
16232 * Returns the currently selected field value or empty string if no value is set.
16233 * @return {String} value The selected value
16235 getValue : function()
16237 if(Roo.isIOS && this.useNativeIOS){
16238 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16242 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16245 if(this.valueField){
16246 return typeof this.value != 'undefined' ? this.value : '';
16248 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16252 getRawValue : function()
16254 if(Roo.isIOS && this.useNativeIOS){
16255 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16258 var v = this.inputEl().getValue();
16264 * Clears any text/value currently set in the field
16266 clearValue : function(){
16268 if(this.hiddenField){
16269 this.hiddenField.dom.value = '';
16272 this.setRawValue('');
16273 this.lastSelectionText = '';
16274 this.lastData = false;
16276 var close = this.closeTriggerEl();
16287 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16288 * will be displayed in the field. If the value does not match the data value of an existing item,
16289 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16290 * Otherwise the field will be blank (although the value will still be set).
16291 * @param {String} value The value to match
16293 setValue : function(v)
16295 if(Roo.isIOS && this.useNativeIOS){
16296 this.setIOSValue(v);
16306 if(this.valueField){
16307 var r = this.findRecord(this.valueField, v);
16309 text = r.data[this.displayField];
16310 }else if(this.valueNotFoundText !== undefined){
16311 text = this.valueNotFoundText;
16314 this.lastSelectionText = text;
16315 if(this.hiddenField){
16316 this.hiddenField.dom.value = v;
16318 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16321 var close = this.closeTriggerEl();
16324 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16330 * @property {Object} the last set data for the element
16335 * Sets the value of the field based on a object which is related to the record format for the store.
16336 * @param {Object} value the value to set as. or false on reset?
16338 setFromData : function(o){
16345 var dv = ''; // display value
16346 var vv = ''; // value value..
16348 if (this.displayField) {
16349 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16351 // this is an error condition!!!
16352 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16355 if(this.valueField){
16356 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16359 var close = this.closeTriggerEl();
16362 if(dv.length || vv * 1 > 0){
16364 this.blockFocus=true;
16370 if(this.hiddenField){
16371 this.hiddenField.dom.value = vv;
16373 this.lastSelectionText = dv;
16374 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16378 // no hidden field.. - we store the value in 'value', but still display
16379 // display field!!!!
16380 this.lastSelectionText = dv;
16381 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16388 reset : function(){
16389 // overridden so that last data is reset..
16396 this.setValue(this.originalValue);
16397 //this.clearInvalid();
16398 this.lastData = false;
16400 this.view.clearSelections();
16406 findRecord : function(prop, value){
16408 if(this.store.getCount() > 0){
16409 this.store.each(function(r){
16410 if(r.data[prop] == value){
16420 getName: function()
16422 // returns hidden if it's set..
16423 if (!this.rendered) {return ''};
16424 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16428 onViewMove : function(e, t){
16429 this.inKeyMode = false;
16433 onViewOver : function(e, t){
16434 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16437 var item = this.view.findItemFromChild(t);
16440 var index = this.view.indexOf(item);
16441 this.select(index, false);
16446 onViewClick : function(view, doFocus, el, e)
16448 var index = this.view.getSelectedIndexes()[0];
16450 var r = this.store.getAt(index);
16454 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16461 Roo.each(this.tickItems, function(v,k){
16463 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16465 _this.tickItems.splice(k, 1);
16467 if(typeof(e) == 'undefined' && view == false){
16468 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16480 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16481 this.tickItems.push(r.data);
16484 if(typeof(e) == 'undefined' && view == false){
16485 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16492 this.onSelect(r, index);
16494 if(doFocus !== false && !this.blockFocus){
16495 this.inputEl().focus();
16500 restrictHeight : function(){
16501 //this.innerList.dom.style.height = '';
16502 //var inner = this.innerList.dom;
16503 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16504 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16505 //this.list.beginUpdate();
16506 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16507 this.list.alignTo(this.inputEl(), this.listAlign);
16508 this.list.alignTo(this.inputEl(), this.listAlign);
16509 //this.list.endUpdate();
16513 onEmptyResults : function(){
16515 if(this.tickable && this.editable){
16516 this.hasFocus = false;
16517 this.restrictHeight();
16525 * Returns true if the dropdown list is expanded, else false.
16527 isExpanded : function(){
16528 return this.list.isVisible();
16532 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16533 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16534 * @param {String} value The data value of the item to select
16535 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16536 * selected item if it is not currently in view (defaults to true)
16537 * @return {Boolean} True if the value matched an item in the list, else false
16539 selectByValue : function(v, scrollIntoView){
16540 if(v !== undefined && v !== null){
16541 var r = this.findRecord(this.valueField || this.displayField, v);
16543 this.select(this.store.indexOf(r), scrollIntoView);
16551 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16552 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16553 * @param {Number} index The zero-based index of the list item to select
16554 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16555 * selected item if it is not currently in view (defaults to true)
16557 select : function(index, scrollIntoView){
16558 this.selectedIndex = index;
16559 this.view.select(index);
16560 if(scrollIntoView !== false){
16561 var el = this.view.getNode(index);
16563 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16566 this.list.scrollChildIntoView(el, false);
16572 selectNext : function(){
16573 var ct = this.store.getCount();
16575 if(this.selectedIndex == -1){
16577 }else if(this.selectedIndex < ct-1){
16578 this.select(this.selectedIndex+1);
16584 selectPrev : function(){
16585 var ct = this.store.getCount();
16587 if(this.selectedIndex == -1){
16589 }else if(this.selectedIndex != 0){
16590 this.select(this.selectedIndex-1);
16596 onKeyUp : function(e){
16597 if(this.editable !== false && !e.isSpecialKey()){
16598 this.lastKey = e.getKey();
16599 this.dqTask.delay(this.queryDelay);
16604 validateBlur : function(){
16605 return !this.list || !this.list.isVisible();
16609 initQuery : function(){
16611 var v = this.getRawValue();
16613 if(this.tickable && this.editable){
16614 v = this.tickableInputEl().getValue();
16621 doForce : function(){
16622 if(this.inputEl().dom.value.length > 0){
16623 this.inputEl().dom.value =
16624 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16630 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16631 * query allowing the query action to be canceled if needed.
16632 * @param {String} query The SQL query to execute
16633 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16634 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16635 * saved in the current store (defaults to false)
16637 doQuery : function(q, forceAll){
16639 if(q === undefined || q === null){
16644 forceAll: forceAll,
16648 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16653 forceAll = qe.forceAll;
16654 if(forceAll === true || (q.length >= this.minChars)){
16656 this.hasQuery = true;
16658 if(this.lastQuery != q || this.alwaysQuery){
16659 this.lastQuery = q;
16660 if(this.mode == 'local'){
16661 this.selectedIndex = -1;
16663 this.store.clearFilter();
16666 if(this.specialFilter){
16667 this.fireEvent('specialfilter', this);
16672 this.store.filter(this.displayField, q);
16675 this.store.fireEvent("datachanged", this.store);
16682 this.store.baseParams[this.queryParam] = q;
16684 var options = {params : this.getParams(q)};
16687 options.add = true;
16688 options.params.start = this.page * this.pageSize;
16691 this.store.load(options);
16694 * this code will make the page width larger, at the beginning, the list not align correctly,
16695 * we should expand the list on onLoad
16696 * so command out it
16701 this.selectedIndex = -1;
16706 this.loadNext = false;
16710 getParams : function(q){
16712 //p[this.queryParam] = q;
16716 p.limit = this.pageSize;
16722 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16724 collapse : function(){
16725 if(!this.isExpanded()){
16731 this.hasFocus = false;
16735 this.cancelBtn.hide();
16736 this.trigger.show();
16739 this.tickableInputEl().dom.value = '';
16740 this.tickableInputEl().blur();
16745 Roo.get(document).un('mousedown', this.collapseIf, this);
16746 Roo.get(document).un('mousewheel', this.collapseIf, this);
16747 if (!this.editable) {
16748 Roo.get(document).un('keydown', this.listKeyPress, this);
16750 this.fireEvent('collapse', this);
16756 collapseIf : function(e){
16757 var in_combo = e.within(this.el);
16758 var in_list = e.within(this.list);
16759 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16761 if (in_combo || in_list || is_list) {
16762 //e.stopPropagation();
16767 this.onTickableFooterButtonClick(e, false, false);
16775 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16777 expand : function(){
16779 if(this.isExpanded() || !this.hasFocus){
16783 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16784 this.list.setWidth(lw);
16790 this.restrictHeight();
16794 this.tickItems = Roo.apply([], this.item);
16797 this.cancelBtn.show();
16798 this.trigger.hide();
16801 this.tickableInputEl().focus();
16806 Roo.get(document).on('mousedown', this.collapseIf, this);
16807 Roo.get(document).on('mousewheel', this.collapseIf, this);
16808 if (!this.editable) {
16809 Roo.get(document).on('keydown', this.listKeyPress, this);
16812 this.fireEvent('expand', this);
16816 // Implements the default empty TriggerField.onTriggerClick function
16817 onTriggerClick : function(e)
16819 Roo.log('trigger click');
16821 if(this.disabled || !this.triggerList){
16826 this.loadNext = false;
16828 if(this.isExpanded()){
16830 if (!this.blockFocus) {
16831 this.inputEl().focus();
16835 this.hasFocus = true;
16836 if(this.triggerAction == 'all') {
16837 this.doQuery(this.allQuery, true);
16839 this.doQuery(this.getRawValue());
16841 if (!this.blockFocus) {
16842 this.inputEl().focus();
16847 onTickableTriggerClick : function(e)
16854 this.loadNext = false;
16855 this.hasFocus = true;
16857 if(this.triggerAction == 'all') {
16858 this.doQuery(this.allQuery, true);
16860 this.doQuery(this.getRawValue());
16864 onSearchFieldClick : function(e)
16866 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16867 this.onTickableFooterButtonClick(e, false, false);
16871 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16876 this.loadNext = false;
16877 this.hasFocus = true;
16879 if(this.triggerAction == 'all') {
16880 this.doQuery(this.allQuery, true);
16882 this.doQuery(this.getRawValue());
16886 listKeyPress : function(e)
16888 //Roo.log('listkeypress');
16889 // scroll to first matching element based on key pres..
16890 if (e.isSpecialKey()) {
16893 var k = String.fromCharCode(e.getKey()).toUpperCase();
16896 var csel = this.view.getSelectedNodes();
16897 var cselitem = false;
16899 var ix = this.view.indexOf(csel[0]);
16900 cselitem = this.store.getAt(ix);
16901 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16907 this.store.each(function(v) {
16909 // start at existing selection.
16910 if (cselitem.id == v.id) {
16916 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16917 match = this.store.indexOf(v);
16923 if (match === false) {
16924 return true; // no more action?
16927 this.view.select(match);
16928 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16929 sn.scrollIntoView(sn.dom.parentNode, false);
16932 onViewScroll : function(e, t){
16934 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){
16938 this.hasQuery = true;
16940 this.loading = this.list.select('.loading', true).first();
16942 if(this.loading === null){
16943 this.list.createChild({
16945 cls: 'loading roo-select2-more-results roo-select2-active',
16946 html: 'Loading more results...'
16949 this.loading = this.list.select('.loading', true).first();
16951 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16953 this.loading.hide();
16956 this.loading.show();
16961 this.loadNext = true;
16963 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16968 addItem : function(o)
16970 var dv = ''; // display value
16972 if (this.displayField) {
16973 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16975 // this is an error condition!!!
16976 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16983 var choice = this.choices.createChild({
16985 cls: 'roo-select2-search-choice',
16994 cls: 'roo-select2-search-choice-close fa fa-times',
16999 }, this.searchField);
17001 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17003 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17011 this.inputEl().dom.value = '';
17016 onRemoveItem : function(e, _self, o)
17018 e.preventDefault();
17020 this.lastItem = Roo.apply([], this.item);
17022 var index = this.item.indexOf(o.data) * 1;
17025 Roo.log('not this item?!');
17029 this.item.splice(index, 1);
17034 this.fireEvent('remove', this, e);
17040 syncValue : function()
17042 if(!this.item.length){
17049 Roo.each(this.item, function(i){
17050 if(_this.valueField){
17051 value.push(i[_this.valueField]);
17058 this.value = value.join(',');
17060 if(this.hiddenField){
17061 this.hiddenField.dom.value = this.value;
17064 this.store.fireEvent("datachanged", this.store);
17069 clearItem : function()
17071 if(!this.multiple){
17077 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17085 if(this.tickable && !Roo.isTouch){
17086 this.view.refresh();
17090 inputEl: function ()
17092 if(Roo.isIOS && this.useNativeIOS){
17093 return this.el.select('select.roo-ios-select', true).first();
17096 if(Roo.isTouch && this.mobileTouchView){
17097 return this.el.select('input.form-control',true).first();
17101 return this.searchField;
17104 return this.el.select('input.form-control',true).first();
17107 onTickableFooterButtonClick : function(e, btn, el)
17109 e.preventDefault();
17111 this.lastItem = Roo.apply([], this.item);
17113 if(btn && btn.name == 'cancel'){
17114 this.tickItems = Roo.apply([], this.item);
17123 Roo.each(this.tickItems, function(o){
17131 validate : function()
17133 if(this.getVisibilityEl().hasClass('hidden')){
17137 var v = this.getRawValue();
17140 v = this.getValue();
17143 if(this.disabled || this.allowBlank || v.length){
17148 this.markInvalid();
17152 tickableInputEl : function()
17154 if(!this.tickable || !this.editable){
17155 return this.inputEl();
17158 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17162 getAutoCreateTouchView : function()
17167 cls: 'form-group' //input-group
17173 type : this.inputType,
17174 cls : 'form-control x-combo-noedit',
17175 autocomplete: 'new-password',
17176 placeholder : this.placeholder || '',
17181 input.name = this.name;
17185 input.cls += ' input-' + this.size;
17188 if (this.disabled) {
17189 input.disabled = true;
17193 cls : 'roo-combobox-wrap',
17200 inputblock.cls += ' input-group';
17202 inputblock.cn.unshift({
17204 cls : 'input-group-addon input-group-prepend input-group-text',
17209 if(this.removable && !this.multiple){
17210 inputblock.cls += ' roo-removable';
17212 inputblock.cn.push({
17215 cls : 'roo-combo-removable-btn close'
17219 if(this.hasFeedback && !this.allowBlank){
17221 inputblock.cls += ' has-feedback';
17223 inputblock.cn.push({
17225 cls: 'glyphicon form-control-feedback'
17232 inputblock.cls += (this.before) ? '' : ' input-group';
17234 inputblock.cn.push({
17236 cls : 'input-group-addon input-group-append input-group-text',
17242 var ibwrap = inputblock;
17247 cls: 'roo-select2-choices',
17251 cls: 'roo-select2-search-field',
17264 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17269 cls: 'form-hidden-field'
17275 if(!this.multiple && this.showToggleBtn){
17281 if (this.caret != false) {
17284 cls: 'fa fa-' + this.caret
17291 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17293 Roo.bootstrap.version == 3 ? caret : '',
17296 cls: 'combobox-clear',
17310 combobox.cls += ' roo-select2-container-multi';
17313 var align = this.labelAlign || this.parentLabelAlign();
17315 if (align ==='left' && this.fieldLabel.length) {
17320 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17321 tooltip : 'This field is required'
17325 cls : 'control-label col-form-label',
17326 html : this.fieldLabel
17330 cls : 'roo-combobox-wrap ',
17337 var labelCfg = cfg.cn[1];
17338 var contentCfg = cfg.cn[2];
17341 if(this.indicatorpos == 'right'){
17346 cls : 'control-label col-form-label',
17350 html : this.fieldLabel
17354 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17355 tooltip : 'This field is required'
17360 cls : "roo-combobox-wrap ",
17368 labelCfg = cfg.cn[0];
17369 contentCfg = cfg.cn[1];
17374 if(this.labelWidth > 12){
17375 labelCfg.style = "width: " + this.labelWidth + 'px';
17378 if(this.labelWidth < 13 && this.labelmd == 0){
17379 this.labelmd = this.labelWidth;
17382 if(this.labellg > 0){
17383 labelCfg.cls += ' col-lg-' + this.labellg;
17384 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17387 if(this.labelmd > 0){
17388 labelCfg.cls += ' col-md-' + this.labelmd;
17389 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17392 if(this.labelsm > 0){
17393 labelCfg.cls += ' col-sm-' + this.labelsm;
17394 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17397 if(this.labelxs > 0){
17398 labelCfg.cls += ' col-xs-' + this.labelxs;
17399 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17403 } else if ( this.fieldLabel.length) {
17407 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17408 tooltip : 'This field is required'
17412 cls : 'control-label',
17413 html : this.fieldLabel
17424 if(this.indicatorpos == 'right'){
17428 cls : 'control-label',
17429 html : this.fieldLabel,
17433 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17434 tooltip : 'This field is required'
17451 var settings = this;
17453 ['xs','sm','md','lg'].map(function(size){
17454 if (settings[size]) {
17455 cfg.cls += ' col-' + size + '-' + settings[size];
17462 initTouchView : function()
17464 this.renderTouchView();
17466 this.touchViewEl.on('scroll', function(){
17467 this.el.dom.scrollTop = 0;
17470 this.originalValue = this.getValue();
17472 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17474 this.inputEl().on("click", this.showTouchView, this);
17475 if (this.triggerEl) {
17476 this.triggerEl.on("click", this.showTouchView, this);
17480 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17481 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17483 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17485 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17486 this.store.on('load', this.onTouchViewLoad, this);
17487 this.store.on('loadexception', this.onTouchViewLoadException, this);
17489 if(this.hiddenName){
17491 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17493 this.hiddenField.dom.value =
17494 this.hiddenValue !== undefined ? this.hiddenValue :
17495 this.value !== undefined ? this.value : '';
17497 this.el.dom.removeAttribute('name');
17498 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17502 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17503 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17506 if(this.removable && !this.multiple){
17507 var close = this.closeTriggerEl();
17509 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17510 close.on('click', this.removeBtnClick, this, close);
17514 * fix the bug in Safari iOS8
17516 this.inputEl().on("focus", function(e){
17517 document.activeElement.blur();
17520 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17527 renderTouchView : function()
17529 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17530 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17532 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17533 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17535 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17536 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17537 this.touchViewBodyEl.setStyle('overflow', 'auto');
17539 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17540 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17542 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17543 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17547 showTouchView : function()
17553 this.touchViewHeaderEl.hide();
17555 if(this.modalTitle.length){
17556 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17557 this.touchViewHeaderEl.show();
17560 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17561 this.touchViewEl.show();
17563 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17565 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17566 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17568 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17570 if(this.modalTitle.length){
17571 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17574 this.touchViewBodyEl.setHeight(bodyHeight);
17578 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17580 this.touchViewEl.addClass(['in','show']);
17583 if(this._touchViewMask){
17584 Roo.get(document.body).addClass("x-body-masked");
17585 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17586 this._touchViewMask.setStyle('z-index', 10000);
17587 this._touchViewMask.addClass('show');
17590 this.doTouchViewQuery();
17594 hideTouchView : function()
17596 this.touchViewEl.removeClass(['in','show']);
17600 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17602 this.touchViewEl.setStyle('display', 'none');
17605 if(this._touchViewMask){
17606 this._touchViewMask.removeClass('show');
17607 Roo.get(document.body).removeClass("x-body-masked");
17611 setTouchViewValue : function()
17618 Roo.each(this.tickItems, function(o){
17623 this.hideTouchView();
17626 doTouchViewQuery : function()
17635 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17639 if(!this.alwaysQuery || this.mode == 'local'){
17640 this.onTouchViewLoad();
17647 onTouchViewBeforeLoad : function(combo,opts)
17653 onTouchViewLoad : function()
17655 if(this.store.getCount() < 1){
17656 this.onTouchViewEmptyResults();
17660 this.clearTouchView();
17662 var rawValue = this.getRawValue();
17664 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17666 this.tickItems = [];
17668 this.store.data.each(function(d, rowIndex){
17669 var row = this.touchViewListGroup.createChild(template);
17671 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17672 row.addClass(d.data.cls);
17675 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17678 html : d.data[this.displayField]
17681 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17682 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17685 row.removeClass('selected');
17686 if(!this.multiple && this.valueField &&
17687 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17690 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17691 row.addClass('selected');
17694 if(this.multiple && this.valueField &&
17695 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17699 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17700 this.tickItems.push(d.data);
17703 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17707 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17709 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17711 if(this.modalTitle.length){
17712 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17715 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17717 if(this.mobile_restrict_height && listHeight < bodyHeight){
17718 this.touchViewBodyEl.setHeight(listHeight);
17723 if(firstChecked && listHeight > bodyHeight){
17724 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17729 onTouchViewLoadException : function()
17731 this.hideTouchView();
17734 onTouchViewEmptyResults : function()
17736 this.clearTouchView();
17738 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17740 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17744 clearTouchView : function()
17746 this.touchViewListGroup.dom.innerHTML = '';
17749 onTouchViewClick : function(e, el, o)
17751 e.preventDefault();
17754 var rowIndex = o.rowIndex;
17756 var r = this.store.getAt(rowIndex);
17758 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17760 if(!this.multiple){
17761 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17762 c.dom.removeAttribute('checked');
17765 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17767 this.setFromData(r.data);
17769 var close = this.closeTriggerEl();
17775 this.hideTouchView();
17777 this.fireEvent('select', this, r, rowIndex);
17782 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17783 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17784 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17788 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17789 this.addItem(r.data);
17790 this.tickItems.push(r.data);
17794 getAutoCreateNativeIOS : function()
17797 cls: 'form-group' //input-group,
17802 cls : 'roo-ios-select'
17806 combobox.name = this.name;
17809 if (this.disabled) {
17810 combobox.disabled = true;
17813 var settings = this;
17815 ['xs','sm','md','lg'].map(function(size){
17816 if (settings[size]) {
17817 cfg.cls += ' col-' + size + '-' + settings[size];
17827 initIOSView : function()
17829 this.store.on('load', this.onIOSViewLoad, this);
17834 onIOSViewLoad : function()
17836 if(this.store.getCount() < 1){
17840 this.clearIOSView();
17842 if(this.allowBlank) {
17844 var default_text = '-- SELECT --';
17846 if(this.placeholder.length){
17847 default_text = this.placeholder;
17850 if(this.emptyTitle.length){
17851 default_text += ' - ' + this.emptyTitle + ' -';
17854 var opt = this.inputEl().createChild({
17857 html : default_text
17861 o[this.valueField] = 0;
17862 o[this.displayField] = default_text;
17864 this.ios_options.push({
17871 this.store.data.each(function(d, rowIndex){
17875 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17876 html = d.data[this.displayField];
17881 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17882 value = d.data[this.valueField];
17891 if(this.value == d.data[this.valueField]){
17892 option['selected'] = true;
17895 var opt = this.inputEl().createChild(option);
17897 this.ios_options.push({
17904 this.inputEl().on('change', function(){
17905 this.fireEvent('select', this);
17910 clearIOSView: function()
17912 this.inputEl().dom.innerHTML = '';
17914 this.ios_options = [];
17917 setIOSValue: function(v)
17921 if(!this.ios_options){
17925 Roo.each(this.ios_options, function(opts){
17927 opts.el.dom.removeAttribute('selected');
17929 if(opts.data[this.valueField] != v){
17933 opts.el.dom.setAttribute('selected', true);
17939 * @cfg {Boolean} grow
17943 * @cfg {Number} growMin
17947 * @cfg {Number} growMax
17956 Roo.apply(Roo.bootstrap.ComboBox, {
17960 cls: 'modal-header',
17982 cls: 'list-group-item',
17986 cls: 'roo-combobox-list-group-item-value'
17990 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18004 listItemCheckbox : {
18006 cls: 'list-group-item',
18010 cls: 'roo-combobox-list-group-item-value'
18014 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18030 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18035 cls: 'modal-footer',
18043 cls: 'col-xs-6 text-left',
18046 cls: 'btn btn-danger roo-touch-view-cancel',
18052 cls: 'col-xs-6 text-right',
18055 cls: 'btn btn-success roo-touch-view-ok',
18066 Roo.apply(Roo.bootstrap.ComboBox, {
18068 touchViewTemplate : {
18070 cls: 'modal fade roo-combobox-touch-view',
18074 cls: 'modal-dialog',
18075 style : 'position:fixed', // we have to fix position....
18079 cls: 'modal-content',
18081 Roo.bootstrap.ComboBox.header,
18082 Roo.bootstrap.ComboBox.body,
18083 Roo.bootstrap.ComboBox.footer
18092 * Ext JS Library 1.1.1
18093 * Copyright(c) 2006-2007, Ext JS, LLC.
18095 * Originally Released Under LGPL - original licence link has changed is not relivant.
18098 * <script type="text/javascript">
18103 * @extends Roo.util.Observable
18104 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18105 * This class also supports single and multi selection modes. <br>
18106 * Create a data model bound view:
18108 var store = new Roo.data.Store(...);
18110 var view = new Roo.View({
18112 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18114 singleSelect: true,
18115 selectedClass: "ydataview-selected",
18119 // listen for node click?
18120 view.on("click", function(vw, index, node, e){
18121 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18125 dataModel.load("foobar.xml");
18127 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18129 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18130 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18132 * Note: old style constructor is still suported (container, template, config)
18135 * Create a new View
18136 * @param {Object} config The config object
18139 Roo.View = function(config, depreciated_tpl, depreciated_config){
18141 this.parent = false;
18143 if (typeof(depreciated_tpl) == 'undefined') {
18144 // new way.. - universal constructor.
18145 Roo.apply(this, config);
18146 this.el = Roo.get(this.el);
18149 this.el = Roo.get(config);
18150 this.tpl = depreciated_tpl;
18151 Roo.apply(this, depreciated_config);
18153 this.wrapEl = this.el.wrap().wrap();
18154 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18157 if(typeof(this.tpl) == "string"){
18158 this.tpl = new Roo.Template(this.tpl);
18160 // support xtype ctors..
18161 this.tpl = new Roo.factory(this.tpl, Roo);
18165 this.tpl.compile();
18170 * @event beforeclick
18171 * Fires before a click is processed. Returns false to cancel the default action.
18172 * @param {Roo.View} this
18173 * @param {Number} index The index of the target node
18174 * @param {HTMLElement} node The target node
18175 * @param {Roo.EventObject} e The raw event object
18177 "beforeclick" : true,
18180 * Fires when a template node is clicked.
18181 * @param {Roo.View} this
18182 * @param {Number} index The index of the target node
18183 * @param {HTMLElement} node The target node
18184 * @param {Roo.EventObject} e The raw event object
18189 * Fires when a template node is double clicked.
18190 * @param {Roo.View} this
18191 * @param {Number} index The index of the target node
18192 * @param {HTMLElement} node The target node
18193 * @param {Roo.EventObject} e The raw event object
18197 * @event contextmenu
18198 * Fires when a template node is right clicked.
18199 * @param {Roo.View} this
18200 * @param {Number} index The index of the target node
18201 * @param {HTMLElement} node The target node
18202 * @param {Roo.EventObject} e The raw event object
18204 "contextmenu" : true,
18206 * @event selectionchange
18207 * Fires when the selected nodes change.
18208 * @param {Roo.View} this
18209 * @param {Array} selections Array of the selected nodes
18211 "selectionchange" : true,
18214 * @event beforeselect
18215 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18216 * @param {Roo.View} this
18217 * @param {HTMLElement} node The node to be selected
18218 * @param {Array} selections Array of currently selected nodes
18220 "beforeselect" : true,
18222 * @event preparedata
18223 * Fires on every row to render, to allow you to change the data.
18224 * @param {Roo.View} this
18225 * @param {Object} data to be rendered (change this)
18227 "preparedata" : true
18235 "click": this.onClick,
18236 "dblclick": this.onDblClick,
18237 "contextmenu": this.onContextMenu,
18241 this.selections = [];
18243 this.cmp = new Roo.CompositeElementLite([]);
18245 this.store = Roo.factory(this.store, Roo.data);
18246 this.setStore(this.store, true);
18249 if ( this.footer && this.footer.xtype) {
18251 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18253 this.footer.dataSource = this.store;
18254 this.footer.container = fctr;
18255 this.footer = Roo.factory(this.footer, Roo);
18256 fctr.insertFirst(this.el);
18258 // this is a bit insane - as the paging toolbar seems to detach the el..
18259 // dom.parentNode.parentNode.parentNode
18260 // they get detached?
18264 Roo.View.superclass.constructor.call(this);
18269 Roo.extend(Roo.View, Roo.util.Observable, {
18272 * @cfg {Roo.data.Store} store Data store to load data from.
18277 * @cfg {String|Roo.Element} el The container element.
18282 * @cfg {String|Roo.Template} tpl The template used by this View
18286 * @cfg {String} dataName the named area of the template to use as the data area
18287 * Works with domtemplates roo-name="name"
18291 * @cfg {String} selectedClass The css class to add to selected nodes
18293 selectedClass : "x-view-selected",
18295 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18300 * @cfg {String} text to display on mask (default Loading)
18304 * @cfg {Boolean} multiSelect Allow multiple selection
18306 multiSelect : false,
18308 * @cfg {Boolean} singleSelect Allow single selection
18310 singleSelect: false,
18313 * @cfg {Boolean} toggleSelect - selecting
18315 toggleSelect : false,
18318 * @cfg {Boolean} tickable - selecting
18323 * Returns the element this view is bound to.
18324 * @return {Roo.Element}
18326 getEl : function(){
18327 return this.wrapEl;
18333 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18335 refresh : function(){
18336 //Roo.log('refresh');
18339 // if we are using something like 'domtemplate', then
18340 // the what gets used is:
18341 // t.applySubtemplate(NAME, data, wrapping data..)
18342 // the outer template then get' applied with
18343 // the store 'extra data'
18344 // and the body get's added to the
18345 // roo-name="data" node?
18346 // <span class='roo-tpl-{name}'></span> ?????
18350 this.clearSelections();
18351 this.el.update("");
18353 var records = this.store.getRange();
18354 if(records.length < 1) {
18356 // is this valid?? = should it render a template??
18358 this.el.update(this.emptyText);
18362 if (this.dataName) {
18363 this.el.update(t.apply(this.store.meta)); //????
18364 el = this.el.child('.roo-tpl-' + this.dataName);
18367 for(var i = 0, len = records.length; i < len; i++){
18368 var data = this.prepareData(records[i].data, i, records[i]);
18369 this.fireEvent("preparedata", this, data, i, records[i]);
18371 var d = Roo.apply({}, data);
18374 Roo.apply(d, {'roo-id' : Roo.id()});
18378 Roo.each(this.parent.item, function(item){
18379 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18382 Roo.apply(d, {'roo-data-checked' : 'checked'});
18386 html[html.length] = Roo.util.Format.trim(
18388 t.applySubtemplate(this.dataName, d, this.store.meta) :
18395 el.update(html.join(""));
18396 this.nodes = el.dom.childNodes;
18397 this.updateIndexes(0);
18402 * Function to override to reformat the data that is sent to
18403 * the template for each node.
18404 * DEPRICATED - use the preparedata event handler.
18405 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18406 * a JSON object for an UpdateManager bound view).
18408 prepareData : function(data, index, record)
18410 this.fireEvent("preparedata", this, data, index, record);
18414 onUpdate : function(ds, record){
18415 // Roo.log('on update');
18416 this.clearSelections();
18417 var index = this.store.indexOf(record);
18418 var n = this.nodes[index];
18419 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18420 n.parentNode.removeChild(n);
18421 this.updateIndexes(index, index);
18427 onAdd : function(ds, records, index)
18429 //Roo.log(['on Add', ds, records, index] );
18430 this.clearSelections();
18431 if(this.nodes.length == 0){
18435 var n = this.nodes[index];
18436 for(var i = 0, len = records.length; i < len; i++){
18437 var d = this.prepareData(records[i].data, i, records[i]);
18439 this.tpl.insertBefore(n, d);
18442 this.tpl.append(this.el, d);
18445 this.updateIndexes(index);
18448 onRemove : function(ds, record, index){
18449 // Roo.log('onRemove');
18450 this.clearSelections();
18451 var el = this.dataName ?
18452 this.el.child('.roo-tpl-' + this.dataName) :
18455 el.dom.removeChild(this.nodes[index]);
18456 this.updateIndexes(index);
18460 * Refresh an individual node.
18461 * @param {Number} index
18463 refreshNode : function(index){
18464 this.onUpdate(this.store, this.store.getAt(index));
18467 updateIndexes : function(startIndex, endIndex){
18468 var ns = this.nodes;
18469 startIndex = startIndex || 0;
18470 endIndex = endIndex || ns.length - 1;
18471 for(var i = startIndex; i <= endIndex; i++){
18472 ns[i].nodeIndex = i;
18477 * Changes the data store this view uses and refresh the view.
18478 * @param {Store} store
18480 setStore : function(store, initial){
18481 if(!initial && this.store){
18482 this.store.un("datachanged", this.refresh);
18483 this.store.un("add", this.onAdd);
18484 this.store.un("remove", this.onRemove);
18485 this.store.un("update", this.onUpdate);
18486 this.store.un("clear", this.refresh);
18487 this.store.un("beforeload", this.onBeforeLoad);
18488 this.store.un("load", this.onLoad);
18489 this.store.un("loadexception", this.onLoad);
18493 store.on("datachanged", this.refresh, this);
18494 store.on("add", this.onAdd, this);
18495 store.on("remove", this.onRemove, this);
18496 store.on("update", this.onUpdate, this);
18497 store.on("clear", this.refresh, this);
18498 store.on("beforeload", this.onBeforeLoad, this);
18499 store.on("load", this.onLoad, this);
18500 store.on("loadexception", this.onLoad, this);
18508 * onbeforeLoad - masks the loading area.
18511 onBeforeLoad : function(store,opts)
18513 //Roo.log('onBeforeLoad');
18515 this.el.update("");
18517 this.el.mask(this.mask ? this.mask : "Loading" );
18519 onLoad : function ()
18526 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18527 * @param {HTMLElement} node
18528 * @return {HTMLElement} The template node
18530 findItemFromChild : function(node){
18531 var el = this.dataName ?
18532 this.el.child('.roo-tpl-' + this.dataName,true) :
18535 if(!node || node.parentNode == el){
18538 var p = node.parentNode;
18539 while(p && p != el){
18540 if(p.parentNode == el){
18549 onClick : function(e){
18550 var item = this.findItemFromChild(e.getTarget());
18552 var index = this.indexOf(item);
18553 if(this.onItemClick(item, index, e) !== false){
18554 this.fireEvent("click", this, index, item, e);
18557 this.clearSelections();
18562 onContextMenu : function(e){
18563 var item = this.findItemFromChild(e.getTarget());
18565 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18570 onDblClick : function(e){
18571 var item = this.findItemFromChild(e.getTarget());
18573 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18577 onItemClick : function(item, index, e)
18579 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18582 if (this.toggleSelect) {
18583 var m = this.isSelected(item) ? 'unselect' : 'select';
18586 _t[m](item, true, false);
18589 if(this.multiSelect || this.singleSelect){
18590 if(this.multiSelect && e.shiftKey && this.lastSelection){
18591 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18593 this.select(item, this.multiSelect && e.ctrlKey);
18594 this.lastSelection = item;
18597 if(!this.tickable){
18598 e.preventDefault();
18606 * Get the number of selected nodes.
18609 getSelectionCount : function(){
18610 return this.selections.length;
18614 * Get the currently selected nodes.
18615 * @return {Array} An array of HTMLElements
18617 getSelectedNodes : function(){
18618 return this.selections;
18622 * Get the indexes of the selected nodes.
18625 getSelectedIndexes : function(){
18626 var indexes = [], s = this.selections;
18627 for(var i = 0, len = s.length; i < len; i++){
18628 indexes.push(s[i].nodeIndex);
18634 * Clear all selections
18635 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18637 clearSelections : function(suppressEvent){
18638 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18639 this.cmp.elements = this.selections;
18640 this.cmp.removeClass(this.selectedClass);
18641 this.selections = [];
18642 if(!suppressEvent){
18643 this.fireEvent("selectionchange", this, this.selections);
18649 * Returns true if the passed node is selected
18650 * @param {HTMLElement/Number} node The node or node index
18651 * @return {Boolean}
18653 isSelected : function(node){
18654 var s = this.selections;
18658 node = this.getNode(node);
18659 return s.indexOf(node) !== -1;
18664 * @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
18665 * @param {Boolean} keepExisting (optional) true to keep existing selections
18666 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18668 select : function(nodeInfo, keepExisting, suppressEvent){
18669 if(nodeInfo instanceof Array){
18671 this.clearSelections(true);
18673 for(var i = 0, len = nodeInfo.length; i < len; i++){
18674 this.select(nodeInfo[i], true, true);
18678 var node = this.getNode(nodeInfo);
18679 if(!node || this.isSelected(node)){
18680 return; // already selected.
18683 this.clearSelections(true);
18686 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18687 Roo.fly(node).addClass(this.selectedClass);
18688 this.selections.push(node);
18689 if(!suppressEvent){
18690 this.fireEvent("selectionchange", this, this.selections);
18698 * @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
18699 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18700 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18702 unselect : function(nodeInfo, keepExisting, suppressEvent)
18704 if(nodeInfo instanceof Array){
18705 Roo.each(this.selections, function(s) {
18706 this.unselect(s, nodeInfo);
18710 var node = this.getNode(nodeInfo);
18711 if(!node || !this.isSelected(node)){
18712 //Roo.log("not selected");
18713 return; // not selected.
18717 Roo.each(this.selections, function(s) {
18719 Roo.fly(node).removeClass(this.selectedClass);
18726 this.selections= ns;
18727 this.fireEvent("selectionchange", this, this.selections);
18731 * Gets a template node.
18732 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18733 * @return {HTMLElement} The node or null if it wasn't found
18735 getNode : function(nodeInfo){
18736 if(typeof nodeInfo == "string"){
18737 return document.getElementById(nodeInfo);
18738 }else if(typeof nodeInfo == "number"){
18739 return this.nodes[nodeInfo];
18745 * Gets a range template nodes.
18746 * @param {Number} startIndex
18747 * @param {Number} endIndex
18748 * @return {Array} An array of nodes
18750 getNodes : function(start, end){
18751 var ns = this.nodes;
18752 start = start || 0;
18753 end = typeof end == "undefined" ? ns.length - 1 : end;
18756 for(var i = start; i <= end; i++){
18760 for(var i = start; i >= end; i--){
18768 * Finds the index of the passed node
18769 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18770 * @return {Number} The index of the node or -1
18772 indexOf : function(node){
18773 node = this.getNode(node);
18774 if(typeof node.nodeIndex == "number"){
18775 return node.nodeIndex;
18777 var ns = this.nodes;
18778 for(var i = 0, len = ns.length; i < len; i++){
18789 * based on jquery fullcalendar
18793 Roo.bootstrap = Roo.bootstrap || {};
18795 * @class Roo.bootstrap.Calendar
18796 * @extends Roo.bootstrap.Component
18797 * Bootstrap Calendar class
18798 * @cfg {Boolean} loadMask (true|false) default false
18799 * @cfg {Object} header generate the user specific header of the calendar, default false
18802 * Create a new Container
18803 * @param {Object} config The config object
18808 Roo.bootstrap.Calendar = function(config){
18809 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18813 * Fires when a date is selected
18814 * @param {DatePicker} this
18815 * @param {Date} date The selected date
18819 * @event monthchange
18820 * Fires when the displayed month changes
18821 * @param {DatePicker} this
18822 * @param {Date} date The selected month
18824 'monthchange': true,
18826 * @event evententer
18827 * Fires when mouse over an event
18828 * @param {Calendar} this
18829 * @param {event} Event
18831 'evententer': true,
18833 * @event eventleave
18834 * Fires when the mouse leaves an
18835 * @param {Calendar} this
18838 'eventleave': true,
18840 * @event eventclick
18841 * Fires when the mouse click an
18842 * @param {Calendar} this
18851 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18854 * @cfg {Number} startDay
18855 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18863 getAutoCreate : function(){
18866 var fc_button = function(name, corner, style, content ) {
18867 return Roo.apply({},{
18869 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18871 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18874 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18885 style : 'width:100%',
18892 cls : 'fc-header-left',
18894 fc_button('prev', 'left', 'arrow', '‹' ),
18895 fc_button('next', 'right', 'arrow', '›' ),
18896 { tag: 'span', cls: 'fc-header-space' },
18897 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18905 cls : 'fc-header-center',
18909 cls: 'fc-header-title',
18912 html : 'month / year'
18920 cls : 'fc-header-right',
18922 /* fc_button('month', 'left', '', 'month' ),
18923 fc_button('week', '', '', 'week' ),
18924 fc_button('day', 'right', '', 'day' )
18936 header = this.header;
18939 var cal_heads = function() {
18941 // fixme - handle this.
18943 for (var i =0; i < Date.dayNames.length; i++) {
18944 var d = Date.dayNames[i];
18947 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18948 html : d.substring(0,3)
18952 ret[0].cls += ' fc-first';
18953 ret[6].cls += ' fc-last';
18956 var cal_cell = function(n) {
18959 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18964 cls: 'fc-day-number',
18968 cls: 'fc-day-content',
18972 style: 'position: relative;' // height: 17px;
18984 var cal_rows = function() {
18987 for (var r = 0; r < 6; r++) {
18994 for (var i =0; i < Date.dayNames.length; i++) {
18995 var d = Date.dayNames[i];
18996 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18999 row.cn[0].cls+=' fc-first';
19000 row.cn[0].cn[0].style = 'min-height:90px';
19001 row.cn[6].cls+=' fc-last';
19005 ret[0].cls += ' fc-first';
19006 ret[4].cls += ' fc-prev-last';
19007 ret[5].cls += ' fc-last';
19014 cls: 'fc-border-separate',
19015 style : 'width:100%',
19023 cls : 'fc-first fc-last',
19041 cls : 'fc-content',
19042 style : "position: relative;",
19045 cls : 'fc-view fc-view-month fc-grid',
19046 style : 'position: relative',
19047 unselectable : 'on',
19050 cls : 'fc-event-container',
19051 style : 'position:absolute;z-index:8;top:0;left:0;'
19069 initEvents : function()
19072 throw "can not find store for calendar";
19078 style: "text-align:center",
19082 style: "background-color:white;width:50%;margin:250 auto",
19086 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19097 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19099 var size = this.el.select('.fc-content', true).first().getSize();
19100 this.maskEl.setSize(size.width, size.height);
19101 this.maskEl.enableDisplayMode("block");
19102 if(!this.loadMask){
19103 this.maskEl.hide();
19106 this.store = Roo.factory(this.store, Roo.data);
19107 this.store.on('load', this.onLoad, this);
19108 this.store.on('beforeload', this.onBeforeLoad, this);
19112 this.cells = this.el.select('.fc-day',true);
19113 //Roo.log(this.cells);
19114 this.textNodes = this.el.query('.fc-day-number');
19115 this.cells.addClassOnOver('fc-state-hover');
19117 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19118 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19119 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19120 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19122 this.on('monthchange', this.onMonthChange, this);
19124 this.update(new Date().clearTime());
19127 resize : function() {
19128 var sz = this.el.getSize();
19130 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19131 this.el.select('.fc-day-content div',true).setHeight(34);
19136 showPrevMonth : function(e){
19137 this.update(this.activeDate.add("mo", -1));
19139 showToday : function(e){
19140 this.update(new Date().clearTime());
19143 showNextMonth : function(e){
19144 this.update(this.activeDate.add("mo", 1));
19148 showPrevYear : function(){
19149 this.update(this.activeDate.add("y", -1));
19153 showNextYear : function(){
19154 this.update(this.activeDate.add("y", 1));
19159 update : function(date)
19161 var vd = this.activeDate;
19162 this.activeDate = date;
19163 // if(vd && this.el){
19164 // var t = date.getTime();
19165 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19166 // Roo.log('using add remove');
19168 // this.fireEvent('monthchange', this, date);
19170 // this.cells.removeClass("fc-state-highlight");
19171 // this.cells.each(function(c){
19172 // if(c.dateValue == t){
19173 // c.addClass("fc-state-highlight");
19174 // setTimeout(function(){
19175 // try{c.dom.firstChild.focus();}catch(e){}
19185 var days = date.getDaysInMonth();
19187 var firstOfMonth = date.getFirstDateOfMonth();
19188 var startingPos = firstOfMonth.getDay()-this.startDay;
19190 if(startingPos < this.startDay){
19194 var pm = date.add(Date.MONTH, -1);
19195 var prevStart = pm.getDaysInMonth()-startingPos;
19197 this.cells = this.el.select('.fc-day',true);
19198 this.textNodes = this.el.query('.fc-day-number');
19199 this.cells.addClassOnOver('fc-state-hover');
19201 var cells = this.cells.elements;
19202 var textEls = this.textNodes;
19204 Roo.each(cells, function(cell){
19205 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19208 days += startingPos;
19210 // convert everything to numbers so it's fast
19211 var day = 86400000;
19212 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19215 //Roo.log(prevStart);
19217 var today = new Date().clearTime().getTime();
19218 var sel = date.clearTime().getTime();
19219 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19220 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19221 var ddMatch = this.disabledDatesRE;
19222 var ddText = this.disabledDatesText;
19223 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19224 var ddaysText = this.disabledDaysText;
19225 var format = this.format;
19227 var setCellClass = function(cal, cell){
19231 //Roo.log('set Cell Class');
19233 var t = d.getTime();
19237 cell.dateValue = t;
19239 cell.className += " fc-today";
19240 cell.className += " fc-state-highlight";
19241 cell.title = cal.todayText;
19244 // disable highlight in other month..
19245 //cell.className += " fc-state-highlight";
19250 cell.className = " fc-state-disabled";
19251 cell.title = cal.minText;
19255 cell.className = " fc-state-disabled";
19256 cell.title = cal.maxText;
19260 if(ddays.indexOf(d.getDay()) != -1){
19261 cell.title = ddaysText;
19262 cell.className = " fc-state-disabled";
19265 if(ddMatch && format){
19266 var fvalue = d.dateFormat(format);
19267 if(ddMatch.test(fvalue)){
19268 cell.title = ddText.replace("%0", fvalue);
19269 cell.className = " fc-state-disabled";
19273 if (!cell.initialClassName) {
19274 cell.initialClassName = cell.dom.className;
19277 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19282 for(; i < startingPos; i++) {
19283 textEls[i].innerHTML = (++prevStart);
19284 d.setDate(d.getDate()+1);
19286 cells[i].className = "fc-past fc-other-month";
19287 setCellClass(this, cells[i]);
19292 for(; i < days; i++){
19293 intDay = i - startingPos + 1;
19294 textEls[i].innerHTML = (intDay);
19295 d.setDate(d.getDate()+1);
19297 cells[i].className = ''; // "x-date-active";
19298 setCellClass(this, cells[i]);
19302 for(; i < 42; i++) {
19303 textEls[i].innerHTML = (++extraDays);
19304 d.setDate(d.getDate()+1);
19306 cells[i].className = "fc-future fc-other-month";
19307 setCellClass(this, cells[i]);
19310 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19312 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19314 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19315 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19317 if(totalRows != 6){
19318 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19319 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19322 this.fireEvent('monthchange', this, date);
19326 if(!this.internalRender){
19327 var main = this.el.dom.firstChild;
19328 var w = main.offsetWidth;
19329 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19330 Roo.fly(main).setWidth(w);
19331 this.internalRender = true;
19332 // opera does not respect the auto grow header center column
19333 // then, after it gets a width opera refuses to recalculate
19334 // without a second pass
19335 if(Roo.isOpera && !this.secondPass){
19336 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19337 this.secondPass = true;
19338 this.update.defer(10, this, [date]);
19345 findCell : function(dt) {
19346 dt = dt.clearTime().getTime();
19348 this.cells.each(function(c){
19349 //Roo.log("check " +c.dateValue + '?=' + dt);
19350 if(c.dateValue == dt){
19360 findCells : function(ev) {
19361 var s = ev.start.clone().clearTime().getTime();
19363 var e= ev.end.clone().clearTime().getTime();
19366 this.cells.each(function(c){
19367 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19369 if(c.dateValue > e){
19372 if(c.dateValue < s){
19381 // findBestRow: function(cells)
19385 // for (var i =0 ; i < cells.length;i++) {
19386 // ret = Math.max(cells[i].rows || 0,ret);
19393 addItem : function(ev)
19395 // look for vertical location slot in
19396 var cells = this.findCells(ev);
19398 // ev.row = this.findBestRow(cells);
19400 // work out the location.
19404 for(var i =0; i < cells.length; i++) {
19406 cells[i].row = cells[0].row;
19409 cells[i].row = cells[i].row + 1;
19419 if (crow.start.getY() == cells[i].getY()) {
19421 crow.end = cells[i];
19438 cells[0].events.push(ev);
19440 this.calevents.push(ev);
19443 clearEvents: function() {
19445 if(!this.calevents){
19449 Roo.each(this.cells.elements, function(c){
19455 Roo.each(this.calevents, function(e) {
19456 Roo.each(e.els, function(el) {
19457 el.un('mouseenter' ,this.onEventEnter, this);
19458 el.un('mouseleave' ,this.onEventLeave, this);
19463 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19469 renderEvents: function()
19473 this.cells.each(function(c) {
19482 if(c.row != c.events.length){
19483 r = 4 - (4 - (c.row - c.events.length));
19486 c.events = ev.slice(0, r);
19487 c.more = ev.slice(r);
19489 if(c.more.length && c.more.length == 1){
19490 c.events.push(c.more.pop());
19493 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19497 this.cells.each(function(c) {
19499 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19502 for (var e = 0; e < c.events.length; e++){
19503 var ev = c.events[e];
19504 var rows = ev.rows;
19506 for(var i = 0; i < rows.length; i++) {
19508 // how many rows should it span..
19511 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19512 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19514 unselectable : "on",
19517 cls: 'fc-event-inner',
19521 // cls: 'fc-event-time',
19522 // html : cells.length > 1 ? '' : ev.time
19526 cls: 'fc-event-title',
19527 html : String.format('{0}', ev.title)
19534 cls: 'ui-resizable-handle ui-resizable-e',
19535 html : '  '
19542 cfg.cls += ' fc-event-start';
19544 if ((i+1) == rows.length) {
19545 cfg.cls += ' fc-event-end';
19548 var ctr = _this.el.select('.fc-event-container',true).first();
19549 var cg = ctr.createChild(cfg);
19551 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19552 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19554 var r = (c.more.length) ? 1 : 0;
19555 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19556 cg.setWidth(ebox.right - sbox.x -2);
19558 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19559 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19560 cg.on('click', _this.onEventClick, _this, ev);
19571 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19572 style : 'position: absolute',
19573 unselectable : "on",
19576 cls: 'fc-event-inner',
19580 cls: 'fc-event-title',
19588 cls: 'ui-resizable-handle ui-resizable-e',
19589 html : '  '
19595 var ctr = _this.el.select('.fc-event-container',true).first();
19596 var cg = ctr.createChild(cfg);
19598 var sbox = c.select('.fc-day-content',true).first().getBox();
19599 var ebox = c.select('.fc-day-content',true).first().getBox();
19601 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19602 cg.setWidth(ebox.right - sbox.x -2);
19604 cg.on('click', _this.onMoreEventClick, _this, c.more);
19614 onEventEnter: function (e, el,event,d) {
19615 this.fireEvent('evententer', this, el, event);
19618 onEventLeave: function (e, el,event,d) {
19619 this.fireEvent('eventleave', this, el, event);
19622 onEventClick: function (e, el,event,d) {
19623 this.fireEvent('eventclick', this, el, event);
19626 onMonthChange: function () {
19630 onMoreEventClick: function(e, el, more)
19634 this.calpopover.placement = 'right';
19635 this.calpopover.setTitle('More');
19637 this.calpopover.setContent('');
19639 var ctr = this.calpopover.el.select('.popover-content', true).first();
19641 Roo.each(more, function(m){
19643 cls : 'fc-event-hori fc-event-draggable',
19646 var cg = ctr.createChild(cfg);
19648 cg.on('click', _this.onEventClick, _this, m);
19651 this.calpopover.show(el);
19656 onLoad: function ()
19658 this.calevents = [];
19661 if(this.store.getCount() > 0){
19662 this.store.data.each(function(d){
19665 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19666 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19667 time : d.data.start_time,
19668 title : d.data.title,
19669 description : d.data.description,
19670 venue : d.data.venue
19675 this.renderEvents();
19677 if(this.calevents.length && this.loadMask){
19678 this.maskEl.hide();
19682 onBeforeLoad: function()
19684 this.clearEvents();
19686 this.maskEl.show();
19700 * @class Roo.bootstrap.Popover
19701 * @extends Roo.bootstrap.Component
19702 * Bootstrap Popover class
19703 * @cfg {String} html contents of the popover (or false to use children..)
19704 * @cfg {String} title of popover (or false to hide)
19705 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19706 * @cfg {String} trigger click || hover (or false to trigger manually)
19707 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19708 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19709 * - if false and it has a 'parent' then it will be automatically added to that element
19710 * - if string - Roo.get will be called
19711 * @cfg {Number} delay - delay before showing
19714 * Create a new Popover
19715 * @param {Object} config The config object
19718 Roo.bootstrap.Popover = function(config){
19719 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19725 * After the popover show
19727 * @param {Roo.bootstrap.Popover} this
19732 * After the popover hide
19734 * @param {Roo.bootstrap.Popover} this
19740 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19745 placement : 'right',
19746 trigger : 'hover', // hover
19752 can_build_overlaid : false,
19754 maskEl : false, // the mask element
19757 alignEl : false, // when show is called with an element - this get's stored.
19759 getChildContainer : function()
19761 return this.contentEl;
19764 getPopoverHeader : function()
19766 this.title = true; // flag not to hide it..
19767 this.headerEl.addClass('p-0');
19768 return this.headerEl
19772 getAutoCreate : function(){
19775 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19776 style: 'display:block',
19782 cls : 'popover-inner ',
19786 cls: 'popover-title popover-header',
19787 html : this.title === false ? '' : this.title
19790 cls : 'popover-content popover-body ' + (this.cls || ''),
19791 html : this.html || ''
19802 * @param {string} the title
19804 setTitle: function(str)
19808 this.headerEl.dom.innerHTML = str;
19813 * @param {string} the body content
19815 setContent: function(str)
19818 if (this.contentEl) {
19819 this.contentEl.dom.innerHTML = str;
19823 // as it get's added to the bottom of the page.
19824 onRender : function(ct, position)
19826 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19831 var cfg = Roo.apply({}, this.getAutoCreate());
19835 cfg.cls += ' ' + this.cls;
19838 cfg.style = this.style;
19840 //Roo.log("adding to ");
19841 this.el = Roo.get(document.body).createChild(cfg, position);
19842 // Roo.log(this.el);
19845 this.contentEl = this.el.select('.popover-content',true).first();
19846 this.headerEl = this.el.select('.popover-title',true).first();
19849 if(typeof(this.items) != 'undefined'){
19850 var items = this.items;
19853 for(var i =0;i < items.length;i++) {
19854 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19858 this.items = nitems;
19860 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19861 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19868 resizeMask : function()
19870 this.maskEl.setSize(
19871 Roo.lib.Dom.getViewWidth(true),
19872 Roo.lib.Dom.getViewHeight(true)
19876 initEvents : function()
19880 Roo.bootstrap.Popover.register(this);
19883 this.arrowEl = this.el.select('.arrow',true).first();
19884 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19885 this.el.enableDisplayMode('block');
19889 if (this.over === false && !this.parent()) {
19892 if (this.triggers === false) {
19897 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19898 var triggers = this.trigger ? this.trigger.split(' ') : [];
19899 Roo.each(triggers, function(trigger) {
19901 if (trigger == 'click') {
19902 on_el.on('click', this.toggle, this);
19903 } else if (trigger != 'manual') {
19904 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19905 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19907 on_el.on(eventIn ,this.enter, this);
19908 on_el.on(eventOut, this.leave, this);
19918 toggle : function () {
19919 this.hoverState == 'in' ? this.leave() : this.enter();
19922 enter : function () {
19924 clearTimeout(this.timeout);
19926 this.hoverState = 'in';
19928 if (!this.delay || !this.delay.show) {
19933 this.timeout = setTimeout(function () {
19934 if (_t.hoverState == 'in') {
19937 }, this.delay.show)
19940 leave : function() {
19941 clearTimeout(this.timeout);
19943 this.hoverState = 'out';
19945 if (!this.delay || !this.delay.hide) {
19950 this.timeout = setTimeout(function () {
19951 if (_t.hoverState == 'out') {
19954 }, this.delay.hide)
19958 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19959 * @param {string} (left|right|top|bottom) position
19961 show : function (on_el, placement)
19963 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19964 on_el = on_el || false; // default to false
19967 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19968 on_el = this.parent().el;
19969 } else if (this.over) {
19970 Roo.get(this.over);
19975 this.alignEl = Roo.get( on_el );
19978 this.render(document.body);
19984 if (this.title === false) {
19985 this.headerEl.hide();
19990 this.el.dom.style.display = 'block';
19993 if (this.alignEl) {
19994 this.updatePosition(this.placement, true);
19997 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19998 var es = this.el.getSize();
19999 var x = Roo.lib.Dom.getViewWidth()/2;
20000 var y = Roo.lib.Dom.getViewHeight()/2;
20001 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20006 //var arrow = this.el.select('.arrow',true).first();
20007 //arrow.set(align[2],
20009 this.el.addClass('in');
20013 this.hoverState = 'in';
20016 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20017 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20018 this.maskEl.dom.style.display = 'block';
20019 this.maskEl.addClass('show');
20021 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20023 this.fireEvent('show', this);
20027 * fire this manually after loading a grid in the table for example
20028 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20029 * @param {Boolean} try and move it if we cant get right position.
20031 updatePosition : function(placement, try_move)
20033 // allow for calling with no parameters
20034 placement = placement ? placement : this.placement;
20035 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20037 this.el.removeClass([
20038 'fade','top','bottom', 'left', 'right','in',
20039 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20041 this.el.addClass(placement + ' bs-popover-' + placement);
20043 if (!this.alignEl ) {
20047 switch (placement) {
20049 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20050 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20051 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20052 //normal display... or moved up/down.
20053 this.el.setXY(offset);
20054 var xy = this.alignEl.getAnchorXY('tr', false);
20056 this.arrowEl.setXY(xy);
20059 // continue through...
20060 return this.updatePosition('left', false);
20064 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20065 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20066 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20067 //normal display... or moved up/down.
20068 this.el.setXY(offset);
20069 var xy = this.alignEl.getAnchorXY('tl', false);
20070 xy[0]-=10;xy[1]+=5; // << fix me
20071 this.arrowEl.setXY(xy);
20075 return this.updatePosition('right', false);
20078 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20079 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20080 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20081 //normal display... or moved up/down.
20082 this.el.setXY(offset);
20083 var xy = this.alignEl.getAnchorXY('t', false);
20084 xy[1]-=10; // << fix me
20085 this.arrowEl.setXY(xy);
20089 return this.updatePosition('bottom', false);
20092 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20093 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20094 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20095 //normal display... or moved up/down.
20096 this.el.setXY(offset);
20097 var xy = this.alignEl.getAnchorXY('b', false);
20098 xy[1]+=2; // << fix me
20099 this.arrowEl.setXY(xy);
20103 return this.updatePosition('top', false);
20114 this.el.setXY([0,0]);
20115 this.el.removeClass('in');
20117 this.hoverState = null;
20118 this.maskEl.hide(); // always..
20119 this.fireEvent('hide', this);
20125 Roo.apply(Roo.bootstrap.Popover, {
20128 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20129 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20130 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20131 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20136 clickHander : false,
20139 onMouseDown : function(e)
20141 if (!e.getTarget(".roo-popover")) {
20149 register : function(popup)
20151 if (!Roo.bootstrap.Popover.clickHandler) {
20152 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20154 // hide other popups.
20156 this.popups.push(popup);
20158 hideAll : function()
20160 this.popups.forEach(function(p) {
20168 * Card header - holder for the card header elements.
20173 * @class Roo.bootstrap.PopoverNav
20174 * @extends Roo.bootstrap.NavGroup
20175 * Bootstrap Popover header navigation class
20177 * Create a new Popover Header Navigation
20178 * @param {Object} config The config object
20181 Roo.bootstrap.PopoverNav = function(config){
20182 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20185 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20188 container_method : 'getPopoverHeader'
20206 * @class Roo.bootstrap.Progress
20207 * @extends Roo.bootstrap.Component
20208 * Bootstrap Progress class
20209 * @cfg {Boolean} striped striped of the progress bar
20210 * @cfg {Boolean} active animated of the progress bar
20214 * Create a new Progress
20215 * @param {Object} config The config object
20218 Roo.bootstrap.Progress = function(config){
20219 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20222 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20227 getAutoCreate : function(){
20235 cfg.cls += ' progress-striped';
20239 cfg.cls += ' active';
20258 * @class Roo.bootstrap.ProgressBar
20259 * @extends Roo.bootstrap.Component
20260 * Bootstrap ProgressBar class
20261 * @cfg {Number} aria_valuenow aria-value now
20262 * @cfg {Number} aria_valuemin aria-value min
20263 * @cfg {Number} aria_valuemax aria-value max
20264 * @cfg {String} label label for the progress bar
20265 * @cfg {String} panel (success | info | warning | danger )
20266 * @cfg {String} role role of the progress bar
20267 * @cfg {String} sr_only text
20271 * Create a new ProgressBar
20272 * @param {Object} config The config object
20275 Roo.bootstrap.ProgressBar = function(config){
20276 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20279 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20283 aria_valuemax : 100,
20289 getAutoCreate : function()
20294 cls: 'progress-bar',
20295 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20307 cfg.role = this.role;
20310 if(this.aria_valuenow){
20311 cfg['aria-valuenow'] = this.aria_valuenow;
20314 if(this.aria_valuemin){
20315 cfg['aria-valuemin'] = this.aria_valuemin;
20318 if(this.aria_valuemax){
20319 cfg['aria-valuemax'] = this.aria_valuemax;
20322 if(this.label && !this.sr_only){
20323 cfg.html = this.label;
20327 cfg.cls += ' progress-bar-' + this.panel;
20333 update : function(aria_valuenow)
20335 this.aria_valuenow = aria_valuenow;
20337 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20352 * @class Roo.bootstrap.TabGroup
20353 * @extends Roo.bootstrap.Column
20354 * Bootstrap Column class
20355 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20356 * @cfg {Boolean} carousel true to make the group behave like a carousel
20357 * @cfg {Boolean} bullets show bullets for the panels
20358 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20359 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20360 * @cfg {Boolean} showarrow (true|false) show arrow default true
20363 * Create a new TabGroup
20364 * @param {Object} config The config object
20367 Roo.bootstrap.TabGroup = function(config){
20368 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20370 this.navId = Roo.id();
20373 Roo.bootstrap.TabGroup.register(this);
20377 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20380 transition : false,
20385 slideOnTouch : false,
20388 getAutoCreate : function()
20390 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20392 cfg.cls += ' tab-content';
20394 if (this.carousel) {
20395 cfg.cls += ' carousel slide';
20398 cls : 'carousel-inner',
20402 if(this.bullets && !Roo.isTouch){
20405 cls : 'carousel-bullets',
20409 if(this.bullets_cls){
20410 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20417 cfg.cn[0].cn.push(bullets);
20420 if(this.showarrow){
20421 cfg.cn[0].cn.push({
20423 class : 'carousel-arrow',
20427 class : 'carousel-prev',
20431 class : 'fa fa-chevron-left'
20437 class : 'carousel-next',
20441 class : 'fa fa-chevron-right'
20454 initEvents: function()
20456 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20457 // this.el.on("touchstart", this.onTouchStart, this);
20460 if(this.autoslide){
20463 this.slideFn = window.setInterval(function() {
20464 _this.showPanelNext();
20468 if(this.showarrow){
20469 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20470 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20476 // onTouchStart : function(e, el, o)
20478 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20482 // this.showPanelNext();
20486 getChildContainer : function()
20488 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20492 * register a Navigation item
20493 * @param {Roo.bootstrap.NavItem} the navitem to add
20495 register : function(item)
20497 this.tabs.push( item);
20498 item.navId = this.navId; // not really needed..
20503 getActivePanel : function()
20506 Roo.each(this.tabs, function(t) {
20516 getPanelByName : function(n)
20519 Roo.each(this.tabs, function(t) {
20520 if (t.tabId == n) {
20528 indexOfPanel : function(p)
20531 Roo.each(this.tabs, function(t,i) {
20532 if (t.tabId == p.tabId) {
20541 * show a specific panel
20542 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20543 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20545 showPanel : function (pan)
20547 if(this.transition || typeof(pan) == 'undefined'){
20548 Roo.log("waiting for the transitionend");
20552 if (typeof(pan) == 'number') {
20553 pan = this.tabs[pan];
20556 if (typeof(pan) == 'string') {
20557 pan = this.getPanelByName(pan);
20560 var cur = this.getActivePanel();
20563 Roo.log('pan or acitve pan is undefined');
20567 if (pan.tabId == this.getActivePanel().tabId) {
20571 if (false === cur.fireEvent('beforedeactivate')) {
20575 if(this.bullets > 0 && !Roo.isTouch){
20576 this.setActiveBullet(this.indexOfPanel(pan));
20579 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20581 //class="carousel-item carousel-item-next carousel-item-left"
20583 this.transition = true;
20584 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20585 var lr = dir == 'next' ? 'left' : 'right';
20586 pan.el.addClass(dir); // or prev
20587 pan.el.addClass('carousel-item-' + dir); // or prev
20588 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20589 cur.el.addClass(lr); // or right
20590 pan.el.addClass(lr);
20591 cur.el.addClass('carousel-item-' +lr); // or right
20592 pan.el.addClass('carousel-item-' +lr);
20596 cur.el.on('transitionend', function() {
20597 Roo.log("trans end?");
20599 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20600 pan.setActive(true);
20602 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20603 cur.setActive(false);
20605 _this.transition = false;
20607 }, this, { single: true } );
20612 cur.setActive(false);
20613 pan.setActive(true);
20618 showPanelNext : function()
20620 var i = this.indexOfPanel(this.getActivePanel());
20622 if (i >= this.tabs.length - 1 && !this.autoslide) {
20626 if (i >= this.tabs.length - 1 && this.autoslide) {
20630 this.showPanel(this.tabs[i+1]);
20633 showPanelPrev : function()
20635 var i = this.indexOfPanel(this.getActivePanel());
20637 if (i < 1 && !this.autoslide) {
20641 if (i < 1 && this.autoslide) {
20642 i = this.tabs.length;
20645 this.showPanel(this.tabs[i-1]);
20649 addBullet: function()
20651 if(!this.bullets || Roo.isTouch){
20654 var ctr = this.el.select('.carousel-bullets',true).first();
20655 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20656 var bullet = ctr.createChild({
20657 cls : 'bullet bullet-' + i
20658 },ctr.dom.lastChild);
20663 bullet.on('click', (function(e, el, o, ii, t){
20665 e.preventDefault();
20667 this.showPanel(ii);
20669 if(this.autoslide && this.slideFn){
20670 clearInterval(this.slideFn);
20671 this.slideFn = window.setInterval(function() {
20672 _this.showPanelNext();
20676 }).createDelegate(this, [i, bullet], true));
20681 setActiveBullet : function(i)
20687 Roo.each(this.el.select('.bullet', true).elements, function(el){
20688 el.removeClass('selected');
20691 var bullet = this.el.select('.bullet-' + i, true).first();
20697 bullet.addClass('selected');
20708 Roo.apply(Roo.bootstrap.TabGroup, {
20712 * register a Navigation Group
20713 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20715 register : function(navgrp)
20717 this.groups[navgrp.navId] = navgrp;
20721 * fetch a Navigation Group based on the navigation ID
20722 * if one does not exist , it will get created.
20723 * @param {string} the navgroup to add
20724 * @returns {Roo.bootstrap.NavGroup} the navgroup
20726 get: function(navId) {
20727 if (typeof(this.groups[navId]) == 'undefined') {
20728 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20730 return this.groups[navId] ;
20745 * @class Roo.bootstrap.TabPanel
20746 * @extends Roo.bootstrap.Component
20747 * Bootstrap TabPanel class
20748 * @cfg {Boolean} active panel active
20749 * @cfg {String} html panel content
20750 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20751 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20752 * @cfg {String} href click to link..
20753 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20757 * Create a new TabPanel
20758 * @param {Object} config The config object
20761 Roo.bootstrap.TabPanel = function(config){
20762 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20766 * Fires when the active status changes
20767 * @param {Roo.bootstrap.TabPanel} this
20768 * @param {Boolean} state the new state
20773 * @event beforedeactivate
20774 * Fires before a tab is de-activated - can be used to do validation on a form.
20775 * @param {Roo.bootstrap.TabPanel} this
20776 * @return {Boolean} false if there is an error
20779 'beforedeactivate': true
20782 this.tabId = this.tabId || Roo.id();
20786 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20793 touchSlide : false,
20794 getAutoCreate : function(){
20799 // item is needed for carousel - not sure if it has any effect otherwise
20800 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20801 html: this.html || ''
20805 cfg.cls += ' active';
20809 cfg.tabId = this.tabId;
20817 initEvents: function()
20819 var p = this.parent();
20821 this.navId = this.navId || p.navId;
20823 if (typeof(this.navId) != 'undefined') {
20824 // not really needed.. but just in case.. parent should be a NavGroup.
20825 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20829 var i = tg.tabs.length - 1;
20831 if(this.active && tg.bullets > 0 && i < tg.bullets){
20832 tg.setActiveBullet(i);
20836 this.el.on('click', this.onClick, this);
20838 if(Roo.isTouch && this.touchSlide){
20839 this.el.on("touchstart", this.onTouchStart, this);
20840 this.el.on("touchmove", this.onTouchMove, this);
20841 this.el.on("touchend", this.onTouchEnd, this);
20846 onRender : function(ct, position)
20848 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20851 setActive : function(state)
20853 Roo.log("panel - set active " + this.tabId + "=" + state);
20855 this.active = state;
20857 this.el.removeClass('active');
20859 } else if (!this.el.hasClass('active')) {
20860 this.el.addClass('active');
20863 this.fireEvent('changed', this, state);
20866 onClick : function(e)
20868 e.preventDefault();
20870 if(!this.href.length){
20874 window.location.href = this.href;
20883 onTouchStart : function(e)
20885 this.swiping = false;
20887 this.startX = e.browserEvent.touches[0].clientX;
20888 this.startY = e.browserEvent.touches[0].clientY;
20891 onTouchMove : function(e)
20893 this.swiping = true;
20895 this.endX = e.browserEvent.touches[0].clientX;
20896 this.endY = e.browserEvent.touches[0].clientY;
20899 onTouchEnd : function(e)
20906 var tabGroup = this.parent();
20908 if(this.endX > this.startX){ // swiping right
20909 tabGroup.showPanelPrev();
20913 if(this.startX > this.endX){ // swiping left
20914 tabGroup.showPanelNext();
20933 * @class Roo.bootstrap.DateField
20934 * @extends Roo.bootstrap.Input
20935 * Bootstrap DateField class
20936 * @cfg {Number} weekStart default 0
20937 * @cfg {String} viewMode default empty, (months|years)
20938 * @cfg {String} minViewMode default empty, (months|years)
20939 * @cfg {Number} startDate default -Infinity
20940 * @cfg {Number} endDate default Infinity
20941 * @cfg {Boolean} todayHighlight default false
20942 * @cfg {Boolean} todayBtn default false
20943 * @cfg {Boolean} calendarWeeks default false
20944 * @cfg {Object} daysOfWeekDisabled default empty
20945 * @cfg {Boolean} singleMode default false (true | false)
20947 * @cfg {Boolean} keyboardNavigation default true
20948 * @cfg {String} language default en
20951 * Create a new DateField
20952 * @param {Object} config The config object
20955 Roo.bootstrap.DateField = function(config){
20956 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20960 * Fires when this field show.
20961 * @param {Roo.bootstrap.DateField} this
20962 * @param {Mixed} date The date value
20967 * Fires when this field hide.
20968 * @param {Roo.bootstrap.DateField} this
20969 * @param {Mixed} date The date value
20974 * Fires when select a date.
20975 * @param {Roo.bootstrap.DateField} this
20976 * @param {Mixed} date The date value
20980 * @event beforeselect
20981 * Fires when before select a date.
20982 * @param {Roo.bootstrap.DateField} this
20983 * @param {Mixed} date The date value
20985 beforeselect : true
20989 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20992 * @cfg {String} format
20993 * The default date format string which can be overriden for localization support. The format must be
20994 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20998 * @cfg {String} altFormats
20999 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21000 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21002 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21010 todayHighlight : false,
21016 keyboardNavigation: true,
21018 calendarWeeks: false,
21020 startDate: -Infinity,
21024 daysOfWeekDisabled: [],
21028 singleMode : false,
21030 UTCDate: function()
21032 return new Date(Date.UTC.apply(Date, arguments));
21035 UTCToday: function()
21037 var today = new Date();
21038 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21041 getDate: function() {
21042 var d = this.getUTCDate();
21043 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21046 getUTCDate: function() {
21050 setDate: function(d) {
21051 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21054 setUTCDate: function(d) {
21056 this.setValue(this.formatDate(this.date));
21059 onRender: function(ct, position)
21062 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21064 this.language = this.language || 'en';
21065 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21066 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21068 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21069 this.format = this.format || 'm/d/y';
21070 this.isInline = false;
21071 this.isInput = true;
21072 this.component = this.el.select('.add-on', true).first() || false;
21073 this.component = (this.component && this.component.length === 0) ? false : this.component;
21074 this.hasInput = this.component && this.inputEl().length;
21076 if (typeof(this.minViewMode === 'string')) {
21077 switch (this.minViewMode) {
21079 this.minViewMode = 1;
21082 this.minViewMode = 2;
21085 this.minViewMode = 0;
21090 if (typeof(this.viewMode === 'string')) {
21091 switch (this.viewMode) {
21104 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21106 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21108 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21110 this.picker().on('mousedown', this.onMousedown, this);
21111 this.picker().on('click', this.onClick, this);
21113 this.picker().addClass('datepicker-dropdown');
21115 this.startViewMode = this.viewMode;
21117 if(this.singleMode){
21118 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21119 v.setVisibilityMode(Roo.Element.DISPLAY);
21123 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21124 v.setStyle('width', '189px');
21128 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21129 if(!this.calendarWeeks){
21134 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21135 v.attr('colspan', function(i, val){
21136 return parseInt(val) + 1;
21141 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21143 this.setStartDate(this.startDate);
21144 this.setEndDate(this.endDate);
21146 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21153 if(this.isInline) {
21158 picker : function()
21160 return this.pickerEl;
21161 // return this.el.select('.datepicker', true).first();
21164 fillDow: function()
21166 var dowCnt = this.weekStart;
21175 if(this.calendarWeeks){
21183 while (dowCnt < this.weekStart + 7) {
21187 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21191 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21194 fillMonths: function()
21197 var months = this.picker().select('>.datepicker-months td', true).first();
21199 months.dom.innerHTML = '';
21205 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21208 months.createChild(month);
21215 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;
21217 if (this.date < this.startDate) {
21218 this.viewDate = new Date(this.startDate);
21219 } else if (this.date > this.endDate) {
21220 this.viewDate = new Date(this.endDate);
21222 this.viewDate = new Date(this.date);
21230 var d = new Date(this.viewDate),
21231 year = d.getUTCFullYear(),
21232 month = d.getUTCMonth(),
21233 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21234 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21235 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21236 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21237 currentDate = this.date && this.date.valueOf(),
21238 today = this.UTCToday();
21240 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21242 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21244 // this.picker.select('>tfoot th.today').
21245 // .text(dates[this.language].today)
21246 // .toggle(this.todayBtn !== false);
21248 this.updateNavArrows();
21251 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21253 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21255 prevMonth.setUTCDate(day);
21257 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21259 var nextMonth = new Date(prevMonth);
21261 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21263 nextMonth = nextMonth.valueOf();
21265 var fillMonths = false;
21267 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21269 while(prevMonth.valueOf() <= nextMonth) {
21272 if (prevMonth.getUTCDay() === this.weekStart) {
21274 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21282 if(this.calendarWeeks){
21283 // ISO 8601: First week contains first thursday.
21284 // ISO also states week starts on Monday, but we can be more abstract here.
21286 // Start of current week: based on weekstart/current date
21287 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21288 // Thursday of this week
21289 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21290 // First Thursday of year, year from thursday
21291 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21292 // Calendar week: ms between thursdays, div ms per day, div 7 days
21293 calWeek = (th - yth) / 864e5 / 7 + 1;
21295 fillMonths.cn.push({
21303 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21305 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21308 if (this.todayHighlight &&
21309 prevMonth.getUTCFullYear() == today.getFullYear() &&
21310 prevMonth.getUTCMonth() == today.getMonth() &&
21311 prevMonth.getUTCDate() == today.getDate()) {
21312 clsName += ' today';
21315 if (currentDate && prevMonth.valueOf() === currentDate) {
21316 clsName += ' active';
21319 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21320 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21321 clsName += ' disabled';
21324 fillMonths.cn.push({
21326 cls: 'day ' + clsName,
21327 html: prevMonth.getDate()
21330 prevMonth.setDate(prevMonth.getDate()+1);
21333 var currentYear = this.date && this.date.getUTCFullYear();
21334 var currentMonth = this.date && this.date.getUTCMonth();
21336 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21338 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21339 v.removeClass('active');
21341 if(currentYear === year && k === currentMonth){
21342 v.addClass('active');
21345 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21346 v.addClass('disabled');
21352 year = parseInt(year/10, 10) * 10;
21354 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21356 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21359 for (var i = -1; i < 11; i++) {
21360 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21362 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21370 showMode: function(dir)
21373 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21376 Roo.each(this.picker().select('>div',true).elements, function(v){
21377 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21380 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21385 if(this.isInline) {
21389 this.picker().removeClass(['bottom', 'top']);
21391 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21393 * place to the top of element!
21397 this.picker().addClass('top');
21398 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21403 this.picker().addClass('bottom');
21405 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21408 parseDate : function(value)
21410 if(!value || value instanceof Date){
21413 var v = Date.parseDate(value, this.format);
21414 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21415 v = Date.parseDate(value, 'Y-m-d');
21417 if(!v && this.altFormats){
21418 if(!this.altFormatsArray){
21419 this.altFormatsArray = this.altFormats.split("|");
21421 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21422 v = Date.parseDate(value, this.altFormatsArray[i]);
21428 formatDate : function(date, fmt)
21430 return (!date || !(date instanceof Date)) ?
21431 date : date.dateFormat(fmt || this.format);
21434 onFocus : function()
21436 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21440 onBlur : function()
21442 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21444 var d = this.inputEl().getValue();
21451 showPopup : function()
21453 this.picker().show();
21457 this.fireEvent('showpopup', this, this.date);
21460 hidePopup : function()
21462 if(this.isInline) {
21465 this.picker().hide();
21466 this.viewMode = this.startViewMode;
21469 this.fireEvent('hidepopup', this, this.date);
21473 onMousedown: function(e)
21475 e.stopPropagation();
21476 e.preventDefault();
21481 Roo.bootstrap.DateField.superclass.keyup.call(this);
21485 setValue: function(v)
21487 if(this.fireEvent('beforeselect', this, v) !== false){
21488 var d = new Date(this.parseDate(v) ).clearTime();
21490 if(isNaN(d.getTime())){
21491 this.date = this.viewDate = '';
21492 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21496 v = this.formatDate(d);
21498 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21500 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21504 this.fireEvent('select', this, this.date);
21508 getValue: function()
21510 return this.formatDate(this.date);
21513 fireKey: function(e)
21515 if (!this.picker().isVisible()){
21516 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21522 var dateChanged = false,
21524 newDate, newViewDate;
21529 e.preventDefault();
21533 if (!this.keyboardNavigation) {
21536 dir = e.keyCode == 37 ? -1 : 1;
21539 newDate = this.moveYear(this.date, dir);
21540 newViewDate = this.moveYear(this.viewDate, dir);
21541 } else if (e.shiftKey){
21542 newDate = this.moveMonth(this.date, dir);
21543 newViewDate = this.moveMonth(this.viewDate, dir);
21545 newDate = new Date(this.date);
21546 newDate.setUTCDate(this.date.getUTCDate() + dir);
21547 newViewDate = new Date(this.viewDate);
21548 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21550 if (this.dateWithinRange(newDate)){
21551 this.date = newDate;
21552 this.viewDate = newViewDate;
21553 this.setValue(this.formatDate(this.date));
21555 e.preventDefault();
21556 dateChanged = true;
21561 if (!this.keyboardNavigation) {
21564 dir = e.keyCode == 38 ? -1 : 1;
21566 newDate = this.moveYear(this.date, dir);
21567 newViewDate = this.moveYear(this.viewDate, dir);
21568 } else if (e.shiftKey){
21569 newDate = this.moveMonth(this.date, dir);
21570 newViewDate = this.moveMonth(this.viewDate, dir);
21572 newDate = new Date(this.date);
21573 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21574 newViewDate = new Date(this.viewDate);
21575 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21577 if (this.dateWithinRange(newDate)){
21578 this.date = newDate;
21579 this.viewDate = newViewDate;
21580 this.setValue(this.formatDate(this.date));
21582 e.preventDefault();
21583 dateChanged = true;
21587 this.setValue(this.formatDate(this.date));
21589 e.preventDefault();
21592 this.setValue(this.formatDate(this.date));
21606 onClick: function(e)
21608 e.stopPropagation();
21609 e.preventDefault();
21611 var target = e.getTarget();
21613 if(target.nodeName.toLowerCase() === 'i'){
21614 target = Roo.get(target).dom.parentNode;
21617 var nodeName = target.nodeName;
21618 var className = target.className;
21619 var html = target.innerHTML;
21620 //Roo.log(nodeName);
21622 switch(nodeName.toLowerCase()) {
21624 switch(className) {
21630 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21631 switch(this.viewMode){
21633 this.viewDate = this.moveMonth(this.viewDate, dir);
21637 this.viewDate = this.moveYear(this.viewDate, dir);
21643 var date = new Date();
21644 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21646 this.setValue(this.formatDate(this.date));
21653 if (className.indexOf('disabled') < 0) {
21654 this.viewDate.setUTCDate(1);
21655 if (className.indexOf('month') > -1) {
21656 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21658 var year = parseInt(html, 10) || 0;
21659 this.viewDate.setUTCFullYear(year);
21663 if(this.singleMode){
21664 this.setValue(this.formatDate(this.viewDate));
21675 //Roo.log(className);
21676 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21677 var day = parseInt(html, 10) || 1;
21678 var year = (this.viewDate || new Date()).getUTCFullYear(),
21679 month = (this.viewDate || new Date()).getUTCMonth();
21681 if (className.indexOf('old') > -1) {
21688 } else if (className.indexOf('new') > -1) {
21696 //Roo.log([year,month,day]);
21697 this.date = this.UTCDate(year, month, day,0,0,0,0);
21698 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21700 //Roo.log(this.formatDate(this.date));
21701 this.setValue(this.formatDate(this.date));
21708 setStartDate: function(startDate)
21710 this.startDate = startDate || -Infinity;
21711 if (this.startDate !== -Infinity) {
21712 this.startDate = this.parseDate(this.startDate);
21715 this.updateNavArrows();
21718 setEndDate: function(endDate)
21720 this.endDate = endDate || Infinity;
21721 if (this.endDate !== Infinity) {
21722 this.endDate = this.parseDate(this.endDate);
21725 this.updateNavArrows();
21728 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21730 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21731 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21732 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21734 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21735 return parseInt(d, 10);
21738 this.updateNavArrows();
21741 updateNavArrows: function()
21743 if(this.singleMode){
21747 var d = new Date(this.viewDate),
21748 year = d.getUTCFullYear(),
21749 month = d.getUTCMonth();
21751 Roo.each(this.picker().select('.prev', true).elements, function(v){
21753 switch (this.viewMode) {
21756 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21762 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21769 Roo.each(this.picker().select('.next', true).elements, function(v){
21771 switch (this.viewMode) {
21774 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21780 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21788 moveMonth: function(date, dir)
21793 var new_date = new Date(date.valueOf()),
21794 day = new_date.getUTCDate(),
21795 month = new_date.getUTCMonth(),
21796 mag = Math.abs(dir),
21798 dir = dir > 0 ? 1 : -1;
21801 // If going back one month, make sure month is not current month
21802 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21804 return new_date.getUTCMonth() == month;
21806 // If going forward one month, make sure month is as expected
21807 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21809 return new_date.getUTCMonth() != new_month;
21811 new_month = month + dir;
21812 new_date.setUTCMonth(new_month);
21813 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21814 if (new_month < 0 || new_month > 11) {
21815 new_month = (new_month + 12) % 12;
21818 // For magnitudes >1, move one month at a time...
21819 for (var i=0; i<mag; i++) {
21820 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21821 new_date = this.moveMonth(new_date, dir);
21823 // ...then reset the day, keeping it in the new month
21824 new_month = new_date.getUTCMonth();
21825 new_date.setUTCDate(day);
21827 return new_month != new_date.getUTCMonth();
21830 // Common date-resetting loop -- if date is beyond end of month, make it
21833 new_date.setUTCDate(--day);
21834 new_date.setUTCMonth(new_month);
21839 moveYear: function(date, dir)
21841 return this.moveMonth(date, dir*12);
21844 dateWithinRange: function(date)
21846 return date >= this.startDate && date <= this.endDate;
21852 this.picker().remove();
21855 validateValue : function(value)
21857 if(this.getVisibilityEl().hasClass('hidden')){
21861 if(value.length < 1) {
21862 if(this.allowBlank){
21868 if(value.length < this.minLength){
21871 if(value.length > this.maxLength){
21875 var vt = Roo.form.VTypes;
21876 if(!vt[this.vtype](value, this)){
21880 if(typeof this.validator == "function"){
21881 var msg = this.validator(value);
21887 if(this.regex && !this.regex.test(value)){
21891 if(typeof(this.parseDate(value)) == 'undefined'){
21895 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21899 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21909 this.date = this.viewDate = '';
21911 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21916 Roo.apply(Roo.bootstrap.DateField, {
21927 html: '<i class="fa fa-arrow-left"/>'
21937 html: '<i class="fa fa-arrow-right"/>'
21979 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21980 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21981 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21982 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21983 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21996 navFnc: 'FullYear',
22001 navFnc: 'FullYear',
22006 Roo.apply(Roo.bootstrap.DateField, {
22010 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22014 cls: 'datepicker-days',
22018 cls: 'table-condensed',
22020 Roo.bootstrap.DateField.head,
22024 Roo.bootstrap.DateField.footer
22031 cls: 'datepicker-months',
22035 cls: 'table-condensed',
22037 Roo.bootstrap.DateField.head,
22038 Roo.bootstrap.DateField.content,
22039 Roo.bootstrap.DateField.footer
22046 cls: 'datepicker-years',
22050 cls: 'table-condensed',
22052 Roo.bootstrap.DateField.head,
22053 Roo.bootstrap.DateField.content,
22054 Roo.bootstrap.DateField.footer
22073 * @class Roo.bootstrap.TimeField
22074 * @extends Roo.bootstrap.Input
22075 * Bootstrap DateField class
22079 * Create a new TimeField
22080 * @param {Object} config The config object
22083 Roo.bootstrap.TimeField = function(config){
22084 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22088 * Fires when this field show.
22089 * @param {Roo.bootstrap.DateField} thisthis
22090 * @param {Mixed} date The date value
22095 * Fires when this field hide.
22096 * @param {Roo.bootstrap.DateField} this
22097 * @param {Mixed} date The date value
22102 * Fires when select a date.
22103 * @param {Roo.bootstrap.DateField} this
22104 * @param {Mixed} date The date value
22110 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22113 * @cfg {String} format
22114 * The default time format string which can be overriden for localization support. The format must be
22115 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22119 getAutoCreate : function()
22121 this.after = '<i class="fa far fa-clock"></i>';
22122 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22126 onRender: function(ct, position)
22129 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22131 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22133 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22135 this.pop = this.picker().select('>.datepicker-time',true).first();
22136 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22138 this.picker().on('mousedown', this.onMousedown, this);
22139 this.picker().on('click', this.onClick, this);
22141 this.picker().addClass('datepicker-dropdown');
22146 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22147 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22148 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22149 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22150 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22151 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22155 fireKey: function(e){
22156 if (!this.picker().isVisible()){
22157 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22163 e.preventDefault();
22171 this.onTogglePeriod();
22174 this.onIncrementMinutes();
22177 this.onDecrementMinutes();
22186 onClick: function(e) {
22187 e.stopPropagation();
22188 e.preventDefault();
22191 picker : function()
22193 return this.pickerEl;
22196 fillTime: function()
22198 var time = this.pop.select('tbody', true).first();
22200 time.dom.innerHTML = '';
22215 cls: 'hours-up fa fas fa-chevron-up'
22235 cls: 'minutes-up fa fas fa-chevron-up'
22256 cls: 'timepicker-hour',
22271 cls: 'timepicker-minute',
22286 cls: 'btn btn-primary period',
22308 cls: 'hours-down fa fas fa-chevron-down'
22328 cls: 'minutes-down fa fas fa-chevron-down'
22346 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22353 var hours = this.time.getHours();
22354 var minutes = this.time.getMinutes();
22367 hours = hours - 12;
22371 hours = '0' + hours;
22375 minutes = '0' + minutes;
22378 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22379 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22380 this.pop.select('button', true).first().dom.innerHTML = period;
22386 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22388 var cls = ['bottom'];
22390 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22397 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22401 //this.picker().setXY(20000,20000);
22402 this.picker().addClass(cls.join('-'));
22406 Roo.each(cls, function(c){
22411 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22412 //_this.picker().setTop(_this.inputEl().getHeight());
22416 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22418 //_this.picker().setTop(0 - _this.picker().getHeight());
22423 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22427 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22435 onFocus : function()
22437 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22441 onBlur : function()
22443 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22449 this.picker().show();
22454 this.fireEvent('show', this, this.date);
22459 this.picker().hide();
22462 this.fireEvent('hide', this, this.date);
22465 setTime : function()
22468 this.setValue(this.time.format(this.format));
22470 this.fireEvent('select', this, this.date);
22475 onMousedown: function(e){
22476 e.stopPropagation();
22477 e.preventDefault();
22480 onIncrementHours: function()
22482 Roo.log('onIncrementHours');
22483 this.time = this.time.add(Date.HOUR, 1);
22488 onDecrementHours: function()
22490 Roo.log('onDecrementHours');
22491 this.time = this.time.add(Date.HOUR, -1);
22495 onIncrementMinutes: function()
22497 Roo.log('onIncrementMinutes');
22498 this.time = this.time.add(Date.MINUTE, 1);
22502 onDecrementMinutes: function()
22504 Roo.log('onDecrementMinutes');
22505 this.time = this.time.add(Date.MINUTE, -1);
22509 onTogglePeriod: function()
22511 Roo.log('onTogglePeriod');
22512 this.time = this.time.add(Date.HOUR, 12);
22520 Roo.apply(Roo.bootstrap.TimeField, {
22524 cls: 'datepicker dropdown-menu',
22528 cls: 'datepicker-time',
22532 cls: 'table-condensed',
22561 cls: 'btn btn-info ok',
22589 * @class Roo.bootstrap.MonthField
22590 * @extends Roo.bootstrap.Input
22591 * Bootstrap MonthField class
22593 * @cfg {String} language default en
22596 * Create a new MonthField
22597 * @param {Object} config The config object
22600 Roo.bootstrap.MonthField = function(config){
22601 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22606 * Fires when this field show.
22607 * @param {Roo.bootstrap.MonthField} this
22608 * @param {Mixed} date The date value
22613 * Fires when this field hide.
22614 * @param {Roo.bootstrap.MonthField} this
22615 * @param {Mixed} date The date value
22620 * Fires when select a date.
22621 * @param {Roo.bootstrap.MonthField} this
22622 * @param {String} oldvalue The old value
22623 * @param {String} newvalue The new value
22629 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22631 onRender: function(ct, position)
22634 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22636 this.language = this.language || 'en';
22637 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22638 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22640 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22641 this.isInline = false;
22642 this.isInput = true;
22643 this.component = this.el.select('.add-on', true).first() || false;
22644 this.component = (this.component && this.component.length === 0) ? false : this.component;
22645 this.hasInput = this.component && this.inputEL().length;
22647 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22649 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22651 this.picker().on('mousedown', this.onMousedown, this);
22652 this.picker().on('click', this.onClick, this);
22654 this.picker().addClass('datepicker-dropdown');
22656 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22657 v.setStyle('width', '189px');
22664 if(this.isInline) {
22670 setValue: function(v, suppressEvent)
22672 var o = this.getValue();
22674 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22678 if(suppressEvent !== true){
22679 this.fireEvent('select', this, o, v);
22684 getValue: function()
22689 onClick: function(e)
22691 e.stopPropagation();
22692 e.preventDefault();
22694 var target = e.getTarget();
22696 if(target.nodeName.toLowerCase() === 'i'){
22697 target = Roo.get(target).dom.parentNode;
22700 var nodeName = target.nodeName;
22701 var className = target.className;
22702 var html = target.innerHTML;
22704 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22708 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22710 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22716 picker : function()
22718 return this.pickerEl;
22721 fillMonths: function()
22724 var months = this.picker().select('>.datepicker-months td', true).first();
22726 months.dom.innerHTML = '';
22732 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22735 months.createChild(month);
22744 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22745 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22748 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22749 e.removeClass('active');
22751 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22752 e.addClass('active');
22759 if(this.isInline) {
22763 this.picker().removeClass(['bottom', 'top']);
22765 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22767 * place to the top of element!
22771 this.picker().addClass('top');
22772 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22777 this.picker().addClass('bottom');
22779 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22782 onFocus : function()
22784 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22788 onBlur : function()
22790 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22792 var d = this.inputEl().getValue();
22801 this.picker().show();
22802 this.picker().select('>.datepicker-months', true).first().show();
22806 this.fireEvent('show', this, this.date);
22811 if(this.isInline) {
22814 this.picker().hide();
22815 this.fireEvent('hide', this, this.date);
22819 onMousedown: function(e)
22821 e.stopPropagation();
22822 e.preventDefault();
22827 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22831 fireKey: function(e)
22833 if (!this.picker().isVisible()){
22834 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22845 e.preventDefault();
22849 dir = e.keyCode == 37 ? -1 : 1;
22851 this.vIndex = this.vIndex + dir;
22853 if(this.vIndex < 0){
22857 if(this.vIndex > 11){
22861 if(isNaN(this.vIndex)){
22865 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22871 dir = e.keyCode == 38 ? -1 : 1;
22873 this.vIndex = this.vIndex + dir * 4;
22875 if(this.vIndex < 0){
22879 if(this.vIndex > 11){
22883 if(isNaN(this.vIndex)){
22887 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22892 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22893 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22897 e.preventDefault();
22900 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22901 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22917 this.picker().remove();
22922 Roo.apply(Roo.bootstrap.MonthField, {
22941 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22942 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22947 Roo.apply(Roo.bootstrap.MonthField, {
22951 cls: 'datepicker dropdown-menu roo-dynamic',
22955 cls: 'datepicker-months',
22959 cls: 'table-condensed',
22961 Roo.bootstrap.DateField.content
22981 * @class Roo.bootstrap.CheckBox
22982 * @extends Roo.bootstrap.Input
22983 * Bootstrap CheckBox class
22985 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22986 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22987 * @cfg {String} boxLabel The text that appears beside the checkbox
22988 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22989 * @cfg {Boolean} checked initnal the element
22990 * @cfg {Boolean} inline inline the element (default false)
22991 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22992 * @cfg {String} tooltip label tooltip
22995 * Create a new CheckBox
22996 * @param {Object} config The config object
22999 Roo.bootstrap.CheckBox = function(config){
23000 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23005 * Fires when the element is checked or unchecked.
23006 * @param {Roo.bootstrap.CheckBox} this This input
23007 * @param {Boolean} checked The new checked value
23012 * Fires when the element is click.
23013 * @param {Roo.bootstrap.CheckBox} this This input
23020 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23022 inputType: 'checkbox',
23031 // checkbox success does not make any sense really..
23036 getAutoCreate : function()
23038 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23044 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23047 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23053 type : this.inputType,
23054 value : this.inputValue,
23055 cls : 'roo-' + this.inputType, //'form-box',
23056 placeholder : this.placeholder || ''
23060 if(this.inputType != 'radio'){
23064 cls : 'roo-hidden-value',
23065 value : this.checked ? this.inputValue : this.valueOff
23070 if (this.weight) { // Validity check?
23071 cfg.cls += " " + this.inputType + "-" + this.weight;
23074 if (this.disabled) {
23075 input.disabled=true;
23079 input.checked = this.checked;
23084 input.name = this.name;
23086 if(this.inputType != 'radio'){
23087 hidden.name = this.name;
23088 input.name = '_hidden_' + this.name;
23093 input.cls += ' input-' + this.size;
23098 ['xs','sm','md','lg'].map(function(size){
23099 if (settings[size]) {
23100 cfg.cls += ' col-' + size + '-' + settings[size];
23104 var inputblock = input;
23106 if (this.before || this.after) {
23109 cls : 'input-group',
23114 inputblock.cn.push({
23116 cls : 'input-group-addon',
23121 inputblock.cn.push(input);
23123 if(this.inputType != 'radio'){
23124 inputblock.cn.push(hidden);
23128 inputblock.cn.push({
23130 cls : 'input-group-addon',
23136 var boxLabelCfg = false;
23142 //'for': id, // box label is handled by onclick - so no for...
23144 html: this.boxLabel
23147 boxLabelCfg.tooltip = this.tooltip;
23153 if (align ==='left' && this.fieldLabel.length) {
23154 // Roo.log("left and has label");
23159 cls : 'control-label',
23160 html : this.fieldLabel
23171 cfg.cn[1].cn.push(boxLabelCfg);
23174 if(this.labelWidth > 12){
23175 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23178 if(this.labelWidth < 13 && this.labelmd == 0){
23179 this.labelmd = this.labelWidth;
23182 if(this.labellg > 0){
23183 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23184 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23187 if(this.labelmd > 0){
23188 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23189 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23192 if(this.labelsm > 0){
23193 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23194 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23197 if(this.labelxs > 0){
23198 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23199 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23202 } else if ( this.fieldLabel.length) {
23203 // Roo.log(" label");
23207 tag: this.boxLabel ? 'span' : 'label',
23209 cls: 'control-label box-input-label',
23210 //cls : 'input-group-addon',
23211 html : this.fieldLabel
23218 cfg.cn.push(boxLabelCfg);
23223 // Roo.log(" no label && no align");
23224 cfg.cn = [ inputblock ] ;
23226 cfg.cn.push(boxLabelCfg);
23234 if(this.inputType != 'radio'){
23235 cfg.cn.push(hidden);
23243 * return the real input element.
23245 inputEl: function ()
23247 return this.el.select('input.roo-' + this.inputType,true).first();
23249 hiddenEl: function ()
23251 return this.el.select('input.roo-hidden-value',true).first();
23254 labelEl: function()
23256 return this.el.select('label.control-label',true).first();
23258 /* depricated... */
23262 return this.labelEl();
23265 boxLabelEl: function()
23267 return this.el.select('label.box-label',true).first();
23270 initEvents : function()
23272 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23274 this.inputEl().on('click', this.onClick, this);
23276 if (this.boxLabel) {
23277 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23280 this.startValue = this.getValue();
23283 Roo.bootstrap.CheckBox.register(this);
23287 onClick : function(e)
23289 if(this.fireEvent('click', this, e) !== false){
23290 this.setChecked(!this.checked);
23295 setChecked : function(state,suppressEvent)
23297 this.startValue = this.getValue();
23299 if(this.inputType == 'radio'){
23301 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23302 e.dom.checked = false;
23305 this.inputEl().dom.checked = true;
23307 this.inputEl().dom.value = this.inputValue;
23309 if(suppressEvent !== true){
23310 this.fireEvent('check', this, true);
23318 this.checked = state;
23320 this.inputEl().dom.checked = state;
23323 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23325 if(suppressEvent !== true){
23326 this.fireEvent('check', this, state);
23332 getValue : function()
23334 if(this.inputType == 'radio'){
23335 return this.getGroupValue();
23338 return this.hiddenEl().dom.value;
23342 getGroupValue : function()
23344 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23348 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23351 setValue : function(v,suppressEvent)
23353 if(this.inputType == 'radio'){
23354 this.setGroupValue(v, suppressEvent);
23358 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23363 setGroupValue : function(v, suppressEvent)
23365 this.startValue = this.getValue();
23367 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23368 e.dom.checked = false;
23370 if(e.dom.value == v){
23371 e.dom.checked = true;
23375 if(suppressEvent !== true){
23376 this.fireEvent('check', this, true);
23384 validate : function()
23386 if(this.getVisibilityEl().hasClass('hidden')){
23392 (this.inputType == 'radio' && this.validateRadio()) ||
23393 (this.inputType == 'checkbox' && this.validateCheckbox())
23399 this.markInvalid();
23403 validateRadio : function()
23405 if(this.getVisibilityEl().hasClass('hidden')){
23409 if(this.allowBlank){
23415 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23416 if(!e.dom.checked){
23428 validateCheckbox : function()
23431 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23432 //return (this.getValue() == this.inputValue) ? true : false;
23435 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23443 for(var i in group){
23444 if(group[i].el.isVisible(true)){
23452 for(var i in group){
23457 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23464 * Mark this field as valid
23466 markValid : function()
23470 this.fireEvent('valid', this);
23472 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23475 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23482 if(this.inputType == 'radio'){
23483 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23484 var fg = e.findParent('.form-group', false, true);
23485 if (Roo.bootstrap.version == 3) {
23486 fg.removeClass([_this.invalidClass, _this.validClass]);
23487 fg.addClass(_this.validClass);
23489 fg.removeClass(['is-valid', 'is-invalid']);
23490 fg.addClass('is-valid');
23498 var fg = this.el.findParent('.form-group', false, true);
23499 if (Roo.bootstrap.version == 3) {
23500 fg.removeClass([this.invalidClass, this.validClass]);
23501 fg.addClass(this.validClass);
23503 fg.removeClass(['is-valid', 'is-invalid']);
23504 fg.addClass('is-valid');
23509 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23515 for(var i in group){
23516 var fg = group[i].el.findParent('.form-group', false, true);
23517 if (Roo.bootstrap.version == 3) {
23518 fg.removeClass([this.invalidClass, this.validClass]);
23519 fg.addClass(this.validClass);
23521 fg.removeClass(['is-valid', 'is-invalid']);
23522 fg.addClass('is-valid');
23528 * Mark this field as invalid
23529 * @param {String} msg The validation message
23531 markInvalid : function(msg)
23533 if(this.allowBlank){
23539 this.fireEvent('invalid', this, msg);
23541 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23544 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23548 label.markInvalid();
23551 if(this.inputType == 'radio'){
23553 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23554 var fg = e.findParent('.form-group', false, true);
23555 if (Roo.bootstrap.version == 3) {
23556 fg.removeClass([_this.invalidClass, _this.validClass]);
23557 fg.addClass(_this.invalidClass);
23559 fg.removeClass(['is-invalid', 'is-valid']);
23560 fg.addClass('is-invalid');
23568 var fg = this.el.findParent('.form-group', false, true);
23569 if (Roo.bootstrap.version == 3) {
23570 fg.removeClass([_this.invalidClass, _this.validClass]);
23571 fg.addClass(_this.invalidClass);
23573 fg.removeClass(['is-invalid', 'is-valid']);
23574 fg.addClass('is-invalid');
23579 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23585 for(var i in group){
23586 var fg = group[i].el.findParent('.form-group', false, true);
23587 if (Roo.bootstrap.version == 3) {
23588 fg.removeClass([_this.invalidClass, _this.validClass]);
23589 fg.addClass(_this.invalidClass);
23591 fg.removeClass(['is-invalid', 'is-valid']);
23592 fg.addClass('is-invalid');
23598 clearInvalid : function()
23600 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23602 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23604 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23606 if (label && label.iconEl) {
23607 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23608 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23612 disable : function()
23614 if(this.inputType != 'radio'){
23615 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23622 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23623 _this.getActionEl().addClass(this.disabledClass);
23624 e.dom.disabled = true;
23628 this.disabled = true;
23629 this.fireEvent("disable", this);
23633 enable : function()
23635 if(this.inputType != 'radio'){
23636 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23643 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23644 _this.getActionEl().removeClass(this.disabledClass);
23645 e.dom.disabled = false;
23649 this.disabled = false;
23650 this.fireEvent("enable", this);
23654 setBoxLabel : function(v)
23659 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23665 Roo.apply(Roo.bootstrap.CheckBox, {
23670 * register a CheckBox Group
23671 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23673 register : function(checkbox)
23675 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23676 this.groups[checkbox.groupId] = {};
23679 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23683 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23687 * fetch a CheckBox Group based on the group ID
23688 * @param {string} the group ID
23689 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23691 get: function(groupId) {
23692 if (typeof(this.groups[groupId]) == 'undefined') {
23696 return this.groups[groupId] ;
23709 * @class Roo.bootstrap.Radio
23710 * @extends Roo.bootstrap.Component
23711 * Bootstrap Radio class
23712 * @cfg {String} boxLabel - the label associated
23713 * @cfg {String} value - the value of radio
23716 * Create a new Radio
23717 * @param {Object} config The config object
23719 Roo.bootstrap.Radio = function(config){
23720 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23724 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23730 getAutoCreate : function()
23734 cls : 'form-group radio',
23739 html : this.boxLabel
23747 initEvents : function()
23749 this.parent().register(this);
23751 this.el.on('click', this.onClick, this);
23755 onClick : function(e)
23757 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23758 this.setChecked(true);
23762 setChecked : function(state, suppressEvent)
23764 this.parent().setValue(this.value, suppressEvent);
23768 setBoxLabel : function(v)
23773 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23788 * @class Roo.bootstrap.SecurePass
23789 * @extends Roo.bootstrap.Input
23790 * Bootstrap SecurePass class
23794 * Create a new SecurePass
23795 * @param {Object} config The config object
23798 Roo.bootstrap.SecurePass = function (config) {
23799 // these go here, so the translation tool can replace them..
23801 PwdEmpty: "Please type a password, and then retype it to confirm.",
23802 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23803 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23804 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23805 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23806 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23807 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23808 TooWeak: "Your password is Too Weak."
23810 this.meterLabel = "Password strength:";
23811 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23812 this.meterClass = [
23813 "roo-password-meter-tooweak",
23814 "roo-password-meter-weak",
23815 "roo-password-meter-medium",
23816 "roo-password-meter-strong",
23817 "roo-password-meter-grey"
23822 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23825 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23827 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23829 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23830 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23831 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23832 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23833 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23834 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23835 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23845 * @cfg {String/Object} Label for the strength meter (defaults to
23846 * 'Password strength:')
23851 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23852 * ['Weak', 'Medium', 'Strong'])
23855 pwdStrengths: false,
23868 initEvents: function ()
23870 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23872 if (this.el.is('input[type=password]') && Roo.isSafari) {
23873 this.el.on('keydown', this.SafariOnKeyDown, this);
23876 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23879 onRender: function (ct, position)
23881 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23882 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23883 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23885 this.trigger.createChild({
23890 cls: 'roo-password-meter-grey col-xs-12',
23893 //width: this.meterWidth + 'px'
23897 cls: 'roo-password-meter-text'
23903 if (this.hideTrigger) {
23904 this.trigger.setDisplayed(false);
23906 this.setSize(this.width || '', this.height || '');
23909 onDestroy: function ()
23911 if (this.trigger) {
23912 this.trigger.removeAllListeners();
23913 this.trigger.remove();
23916 this.wrap.remove();
23918 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23921 checkStrength: function ()
23923 var pwd = this.inputEl().getValue();
23924 if (pwd == this._lastPwd) {
23929 if (this.ClientSideStrongPassword(pwd)) {
23931 } else if (this.ClientSideMediumPassword(pwd)) {
23933 } else if (this.ClientSideWeakPassword(pwd)) {
23939 Roo.log('strength1: ' + strength);
23941 //var pm = this.trigger.child('div/div/div').dom;
23942 var pm = this.trigger.child('div/div');
23943 pm.removeClass(this.meterClass);
23944 pm.addClass(this.meterClass[strength]);
23947 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23949 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23951 this._lastPwd = pwd;
23955 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23957 this._lastPwd = '';
23959 var pm = this.trigger.child('div/div');
23960 pm.removeClass(this.meterClass);
23961 pm.addClass('roo-password-meter-grey');
23964 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23967 this.inputEl().dom.type='password';
23970 validateValue: function (value)
23972 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23975 if (value.length == 0) {
23976 if (this.allowBlank) {
23977 this.clearInvalid();
23981 this.markInvalid(this.errors.PwdEmpty);
23982 this.errorMsg = this.errors.PwdEmpty;
23990 if (!value.match(/[\x21-\x7e]+/)) {
23991 this.markInvalid(this.errors.PwdBadChar);
23992 this.errorMsg = this.errors.PwdBadChar;
23995 if (value.length < 6) {
23996 this.markInvalid(this.errors.PwdShort);
23997 this.errorMsg = this.errors.PwdShort;
24000 if (value.length > 16) {
24001 this.markInvalid(this.errors.PwdLong);
24002 this.errorMsg = this.errors.PwdLong;
24006 if (this.ClientSideStrongPassword(value)) {
24008 } else if (this.ClientSideMediumPassword(value)) {
24010 } else if (this.ClientSideWeakPassword(value)) {
24017 if (strength < 2) {
24018 //this.markInvalid(this.errors.TooWeak);
24019 this.errorMsg = this.errors.TooWeak;
24024 console.log('strength2: ' + strength);
24026 //var pm = this.trigger.child('div/div/div').dom;
24028 var pm = this.trigger.child('div/div');
24029 pm.removeClass(this.meterClass);
24030 pm.addClass(this.meterClass[strength]);
24032 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24034 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24036 this.errorMsg = '';
24040 CharacterSetChecks: function (type)
24043 this.fResult = false;
24046 isctype: function (character, type)
24049 case this.kCapitalLetter:
24050 if (character >= 'A' && character <= 'Z') {
24055 case this.kSmallLetter:
24056 if (character >= 'a' && character <= 'z') {
24062 if (character >= '0' && character <= '9') {
24067 case this.kPunctuation:
24068 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24079 IsLongEnough: function (pwd, size)
24081 return !(pwd == null || isNaN(size) || pwd.length < size);
24084 SpansEnoughCharacterSets: function (word, nb)
24086 if (!this.IsLongEnough(word, nb))
24091 var characterSetChecks = new Array(
24092 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24093 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24096 for (var index = 0; index < word.length; ++index) {
24097 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24098 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24099 characterSetChecks[nCharSet].fResult = true;
24106 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24107 if (characterSetChecks[nCharSet].fResult) {
24112 if (nCharSets < nb) {
24118 ClientSideStrongPassword: function (pwd)
24120 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24123 ClientSideMediumPassword: function (pwd)
24125 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24128 ClientSideWeakPassword: function (pwd)
24130 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24133 })//<script type="text/javascript">
24136 * Based Ext JS Library 1.1.1
24137 * Copyright(c) 2006-2007, Ext JS, LLC.
24143 * @class Roo.HtmlEditorCore
24144 * @extends Roo.Component
24145 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24147 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24150 Roo.HtmlEditorCore = function(config){
24153 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24158 * @event initialize
24159 * Fires when the editor is fully initialized (including the iframe)
24160 * @param {Roo.HtmlEditorCore} this
24165 * Fires when the editor is first receives the focus. Any insertion must wait
24166 * until after this event.
24167 * @param {Roo.HtmlEditorCore} this
24171 * @event beforesync
24172 * Fires before the textarea is updated with content from the editor iframe. Return false
24173 * to cancel the sync.
24174 * @param {Roo.HtmlEditorCore} this
24175 * @param {String} html
24179 * @event beforepush
24180 * Fires before the iframe editor is updated with content from the textarea. Return false
24181 * to cancel the push.
24182 * @param {Roo.HtmlEditorCore} this
24183 * @param {String} html
24188 * Fires when the textarea is updated with content from the editor iframe.
24189 * @param {Roo.HtmlEditorCore} this
24190 * @param {String} html
24195 * Fires when the iframe editor is updated with content from the textarea.
24196 * @param {Roo.HtmlEditorCore} this
24197 * @param {String} html
24202 * @event editorevent
24203 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24204 * @param {Roo.HtmlEditorCore} this
24210 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24212 // defaults : white / black...
24213 this.applyBlacklists();
24220 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24224 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24230 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24235 * @cfg {Number} height (in pixels)
24239 * @cfg {Number} width (in pixels)
24244 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24247 stylesheets: false,
24252 // private properties
24253 validationEvent : false,
24255 initialized : false,
24257 sourceEditMode : false,
24258 onFocus : Roo.emptyFn,
24260 hideMode:'offsets',
24264 // blacklist + whitelisted elements..
24271 * Protected method that will not generally be called directly. It
24272 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24273 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24275 getDocMarkup : function(){
24279 // inherit styels from page...??
24280 if (this.stylesheets === false) {
24282 Roo.get(document.head).select('style').each(function(node) {
24283 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24286 Roo.get(document.head).select('link').each(function(node) {
24287 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24290 } else if (!this.stylesheets.length) {
24292 st = '<style type="text/css">' +
24293 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24296 for (var i in this.stylesheets) {
24297 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24302 st += '<style type="text/css">' +
24303 'IMG { cursor: pointer } ' +
24306 var cls = 'roo-htmleditor-body';
24308 if(this.bodyCls.length){
24309 cls += ' ' + this.bodyCls;
24312 return '<html><head>' + st +
24313 //<style type="text/css">' +
24314 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24316 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24320 onRender : function(ct, position)
24323 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24324 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24327 this.el.dom.style.border = '0 none';
24328 this.el.dom.setAttribute('tabIndex', -1);
24329 this.el.addClass('x-hidden hide');
24333 if(Roo.isIE){ // fix IE 1px bogus margin
24334 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24338 this.frameId = Roo.id();
24342 var iframe = this.owner.wrap.createChild({
24344 cls: 'form-control', // bootstrap..
24346 name: this.frameId,
24347 frameBorder : 'no',
24348 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24353 this.iframe = iframe.dom;
24355 this.assignDocWin();
24357 this.doc.designMode = 'on';
24360 this.doc.write(this.getDocMarkup());
24364 var task = { // must defer to wait for browser to be ready
24366 //console.log("run task?" + this.doc.readyState);
24367 this.assignDocWin();
24368 if(this.doc.body || this.doc.readyState == 'complete'){
24370 this.doc.designMode="on";
24374 Roo.TaskMgr.stop(task);
24375 this.initEditor.defer(10, this);
24382 Roo.TaskMgr.start(task);
24387 onResize : function(w, h)
24389 Roo.log('resize: ' +w + ',' + h );
24390 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24394 if(typeof w == 'number'){
24396 this.iframe.style.width = w + 'px';
24398 if(typeof h == 'number'){
24400 this.iframe.style.height = h + 'px';
24402 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24409 * Toggles the editor between standard and source edit mode.
24410 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24412 toggleSourceEdit : function(sourceEditMode){
24414 this.sourceEditMode = sourceEditMode === true;
24416 if(this.sourceEditMode){
24418 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24421 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24422 //this.iframe.className = '';
24425 //this.setSize(this.owner.wrap.getSize());
24426 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24433 * Protected method that will not generally be called directly. If you need/want
24434 * custom HTML cleanup, this is the method you should override.
24435 * @param {String} html The HTML to be cleaned
24436 * return {String} The cleaned HTML
24438 cleanHtml : function(html){
24439 html = String(html);
24440 if(html.length > 5){
24441 if(Roo.isSafari){ // strip safari nonsense
24442 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24445 if(html == ' '){
24452 * HTML Editor -> Textarea
24453 * Protected method that will not generally be called directly. Syncs the contents
24454 * of the editor iframe with the textarea.
24456 syncValue : function(){
24457 if(this.initialized){
24458 var bd = (this.doc.body || this.doc.documentElement);
24459 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24460 var html = bd.innerHTML;
24462 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24463 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24465 html = '<div style="'+m[0]+'">' + html + '</div>';
24468 html = this.cleanHtml(html);
24469 // fix up the special chars.. normaly like back quotes in word...
24470 // however we do not want to do this with chinese..
24471 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24473 var cc = match.charCodeAt();
24475 // Get the character value, handling surrogate pairs
24476 if (match.length == 2) {
24477 // It's a surrogate pair, calculate the Unicode code point
24478 var high = match.charCodeAt(0) - 0xD800;
24479 var low = match.charCodeAt(1) - 0xDC00;
24480 cc = (high * 0x400) + low + 0x10000;
24482 (cc >= 0x4E00 && cc < 0xA000 ) ||
24483 (cc >= 0x3400 && cc < 0x4E00 ) ||
24484 (cc >= 0xf900 && cc < 0xfb00 )
24489 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24490 return "&#" + cc + ";";
24497 if(this.owner.fireEvent('beforesync', this, html) !== false){
24498 this.el.dom.value = html;
24499 this.owner.fireEvent('sync', this, html);
24505 * Protected method that will not generally be called directly. Pushes the value of the textarea
24506 * into the iframe editor.
24508 pushValue : function(){
24509 if(this.initialized){
24510 var v = this.el.dom.value.trim();
24512 // if(v.length < 1){
24516 if(this.owner.fireEvent('beforepush', this, v) !== false){
24517 var d = (this.doc.body || this.doc.documentElement);
24519 this.cleanUpPaste();
24520 this.el.dom.value = d.innerHTML;
24521 this.owner.fireEvent('push', this, v);
24527 deferFocus : function(){
24528 this.focus.defer(10, this);
24532 focus : function(){
24533 if(this.win && !this.sourceEditMode){
24540 assignDocWin: function()
24542 var iframe = this.iframe;
24545 this.doc = iframe.contentWindow.document;
24546 this.win = iframe.contentWindow;
24548 // if (!Roo.get(this.frameId)) {
24551 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24552 // this.win = Roo.get(this.frameId).dom.contentWindow;
24554 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24558 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24559 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24564 initEditor : function(){
24565 //console.log("INIT EDITOR");
24566 this.assignDocWin();
24570 this.doc.designMode="on";
24572 this.doc.write(this.getDocMarkup());
24575 var dbody = (this.doc.body || this.doc.documentElement);
24576 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24577 // this copies styles from the containing element into thsi one..
24578 // not sure why we need all of this..
24579 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24581 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24582 //ss['background-attachment'] = 'fixed'; // w3c
24583 dbody.bgProperties = 'fixed'; // ie
24584 //Roo.DomHelper.applyStyles(dbody, ss);
24585 Roo.EventManager.on(this.doc, {
24586 //'mousedown': this.onEditorEvent,
24587 'mouseup': this.onEditorEvent,
24588 'dblclick': this.onEditorEvent,
24589 'click': this.onEditorEvent,
24590 'keyup': this.onEditorEvent,
24595 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24597 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24598 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24600 this.initialized = true;
24602 this.owner.fireEvent('initialize', this);
24607 onDestroy : function(){
24613 //for (var i =0; i < this.toolbars.length;i++) {
24614 // // fixme - ask toolbars for heights?
24615 // this.toolbars[i].onDestroy();
24618 //this.wrap.dom.innerHTML = '';
24619 //this.wrap.remove();
24624 onFirstFocus : function(){
24626 this.assignDocWin();
24629 this.activated = true;
24632 if(Roo.isGecko){ // prevent silly gecko errors
24634 var s = this.win.getSelection();
24635 if(!s.focusNode || s.focusNode.nodeType != 3){
24636 var r = s.getRangeAt(0);
24637 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24642 this.execCmd('useCSS', true);
24643 this.execCmd('styleWithCSS', false);
24646 this.owner.fireEvent('activate', this);
24650 adjustFont: function(btn){
24651 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24652 //if(Roo.isSafari){ // safari
24655 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24656 if(Roo.isSafari){ // safari
24657 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24658 v = (v < 10) ? 10 : v;
24659 v = (v > 48) ? 48 : v;
24660 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24665 v = Math.max(1, v+adjust);
24667 this.execCmd('FontSize', v );
24670 onEditorEvent : function(e)
24672 this.owner.fireEvent('editorevent', this, e);
24673 // this.updateToolbar();
24674 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24677 insertTag : function(tg)
24679 // could be a bit smarter... -> wrap the current selected tRoo..
24680 if (tg.toLowerCase() == 'span' ||
24681 tg.toLowerCase() == 'code' ||
24682 tg.toLowerCase() == 'sup' ||
24683 tg.toLowerCase() == 'sub'
24686 range = this.createRange(this.getSelection());
24687 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24688 wrappingNode.appendChild(range.extractContents());
24689 range.insertNode(wrappingNode);
24696 this.execCmd("formatblock", tg);
24700 insertText : function(txt)
24704 var range = this.createRange();
24705 range.deleteContents();
24706 //alert(Sender.getAttribute('label'));
24708 range.insertNode(this.doc.createTextNode(txt));
24714 * Executes a Midas editor command on the editor document and performs necessary focus and
24715 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24716 * @param {String} cmd The Midas command
24717 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24719 relayCmd : function(cmd, value){
24721 this.execCmd(cmd, value);
24722 this.owner.fireEvent('editorevent', this);
24723 //this.updateToolbar();
24724 this.owner.deferFocus();
24728 * Executes a Midas editor command directly on the editor document.
24729 * For visual commands, you should use {@link #relayCmd} instead.
24730 * <b>This should only be called after the editor is initialized.</b>
24731 * @param {String} cmd The Midas command
24732 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24734 execCmd : function(cmd, value){
24735 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24742 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24744 * @param {String} text | dom node..
24746 insertAtCursor : function(text)
24749 if(!this.activated){
24755 var r = this.doc.selection.createRange();
24766 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24770 // from jquery ui (MIT licenced)
24772 var win = this.win;
24774 if (win.getSelection && win.getSelection().getRangeAt) {
24775 range = win.getSelection().getRangeAt(0);
24776 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24777 range.insertNode(node);
24778 } else if (win.document.selection && win.document.selection.createRange) {
24779 // no firefox support
24780 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24781 win.document.selection.createRange().pasteHTML(txt);
24783 // no firefox support
24784 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24785 this.execCmd('InsertHTML', txt);
24794 mozKeyPress : function(e){
24796 var c = e.getCharCode(), cmd;
24799 c = String.fromCharCode(c).toLowerCase();
24813 this.cleanUpPaste.defer(100, this);
24821 e.preventDefault();
24829 fixKeys : function(){ // load time branching for fastest keydown performance
24831 return function(e){
24832 var k = e.getKey(), r;
24835 r = this.doc.selection.createRange();
24838 r.pasteHTML('    ');
24845 r = this.doc.selection.createRange();
24847 var target = r.parentElement();
24848 if(!target || target.tagName.toLowerCase() != 'li'){
24850 r.pasteHTML('<br />');
24856 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24857 this.cleanUpPaste.defer(100, this);
24863 }else if(Roo.isOpera){
24864 return function(e){
24865 var k = e.getKey();
24869 this.execCmd('InsertHTML','    ');
24872 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24873 this.cleanUpPaste.defer(100, this);
24878 }else if(Roo.isSafari){
24879 return function(e){
24880 var k = e.getKey();
24884 this.execCmd('InsertText','\t');
24888 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24889 this.cleanUpPaste.defer(100, this);
24897 getAllAncestors: function()
24899 var p = this.getSelectedNode();
24902 a.push(p); // push blank onto stack..
24903 p = this.getParentElement();
24907 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24911 a.push(this.doc.body);
24915 lastSelNode : false,
24918 getSelection : function()
24920 this.assignDocWin();
24921 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24924 getSelectedNode: function()
24926 // this may only work on Gecko!!!
24928 // should we cache this!!!!
24933 var range = this.createRange(this.getSelection()).cloneRange();
24936 var parent = range.parentElement();
24938 var testRange = range.duplicate();
24939 testRange.moveToElementText(parent);
24940 if (testRange.inRange(range)) {
24943 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24946 parent = parent.parentElement;
24951 // is ancestor a text element.
24952 var ac = range.commonAncestorContainer;
24953 if (ac.nodeType == 3) {
24954 ac = ac.parentNode;
24957 var ar = ac.childNodes;
24960 var other_nodes = [];
24961 var has_other_nodes = false;
24962 for (var i=0;i<ar.length;i++) {
24963 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24966 // fullly contained node.
24968 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24973 // probably selected..
24974 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24975 other_nodes.push(ar[i]);
24979 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24984 has_other_nodes = true;
24986 if (!nodes.length && other_nodes.length) {
24987 nodes= other_nodes;
24989 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24995 createRange: function(sel)
24997 // this has strange effects when using with
24998 // top toolbar - not sure if it's a great idea.
24999 //this.editor.contentWindow.focus();
25000 if (typeof sel != "undefined") {
25002 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25004 return this.doc.createRange();
25007 return this.doc.createRange();
25010 getParentElement: function()
25013 this.assignDocWin();
25014 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25016 var range = this.createRange(sel);
25019 var p = range.commonAncestorContainer;
25020 while (p.nodeType == 3) { // text node
25031 * Range intersection.. the hard stuff...
25035 * [ -- selected range --- ]
25039 * if end is before start or hits it. fail.
25040 * if start is after end or hits it fail.
25042 * if either hits (but other is outside. - then it's not
25048 // @see http://www.thismuchiknow.co.uk/?p=64.
25049 rangeIntersectsNode : function(range, node)
25051 var nodeRange = node.ownerDocument.createRange();
25053 nodeRange.selectNode(node);
25055 nodeRange.selectNodeContents(node);
25058 var rangeStartRange = range.cloneRange();
25059 rangeStartRange.collapse(true);
25061 var rangeEndRange = range.cloneRange();
25062 rangeEndRange.collapse(false);
25064 var nodeStartRange = nodeRange.cloneRange();
25065 nodeStartRange.collapse(true);
25067 var nodeEndRange = nodeRange.cloneRange();
25068 nodeEndRange.collapse(false);
25070 return rangeStartRange.compareBoundaryPoints(
25071 Range.START_TO_START, nodeEndRange) == -1 &&
25072 rangeEndRange.compareBoundaryPoints(
25073 Range.START_TO_START, nodeStartRange) == 1;
25077 rangeCompareNode : function(range, node)
25079 var nodeRange = node.ownerDocument.createRange();
25081 nodeRange.selectNode(node);
25083 nodeRange.selectNodeContents(node);
25087 range.collapse(true);
25089 nodeRange.collapse(true);
25091 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25092 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25094 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25096 var nodeIsBefore = ss == 1;
25097 var nodeIsAfter = ee == -1;
25099 if (nodeIsBefore && nodeIsAfter) {
25102 if (!nodeIsBefore && nodeIsAfter) {
25103 return 1; //right trailed.
25106 if (nodeIsBefore && !nodeIsAfter) {
25107 return 2; // left trailed.
25113 // private? - in a new class?
25114 cleanUpPaste : function()
25116 // cleans up the whole document..
25117 Roo.log('cleanuppaste');
25119 this.cleanUpChildren(this.doc.body);
25120 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25121 if (clean != this.doc.body.innerHTML) {
25122 this.doc.body.innerHTML = clean;
25127 cleanWordChars : function(input) {// change the chars to hex code
25128 var he = Roo.HtmlEditorCore;
25130 var output = input;
25131 Roo.each(he.swapCodes, function(sw) {
25132 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25134 output = output.replace(swapper, sw[1]);
25141 cleanUpChildren : function (n)
25143 if (!n.childNodes.length) {
25146 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25147 this.cleanUpChild(n.childNodes[i]);
25154 cleanUpChild : function (node)
25157 //console.log(node);
25158 if (node.nodeName == "#text") {
25159 // clean up silly Windows -- stuff?
25162 if (node.nodeName == "#comment") {
25163 node.parentNode.removeChild(node);
25164 // clean up silly Windows -- stuff?
25167 var lcname = node.tagName.toLowerCase();
25168 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25169 // whitelist of tags..
25171 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25173 node.parentNode.removeChild(node);
25178 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25180 // spans with no attributes - just remove them..
25181 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25182 remove_keep_children = true;
25185 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25186 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25188 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25189 // remove_keep_children = true;
25192 if (remove_keep_children) {
25193 this.cleanUpChildren(node);
25194 // inserts everything just before this node...
25195 while (node.childNodes.length) {
25196 var cn = node.childNodes[0];
25197 node.removeChild(cn);
25198 node.parentNode.insertBefore(cn, node);
25200 node.parentNode.removeChild(node);
25204 if (!node.attributes || !node.attributes.length) {
25209 this.cleanUpChildren(node);
25213 function cleanAttr(n,v)
25216 if (v.match(/^\./) || v.match(/^\//)) {
25219 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25222 if (v.match(/^#/)) {
25225 if (v.match(/^\{/)) { // allow template editing.
25228 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25229 node.removeAttribute(n);
25233 var cwhite = this.cwhite;
25234 var cblack = this.cblack;
25236 function cleanStyle(n,v)
25238 if (v.match(/expression/)) { //XSS?? should we even bother..
25239 node.removeAttribute(n);
25243 var parts = v.split(/;/);
25246 Roo.each(parts, function(p) {
25247 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25251 var l = p.split(':').shift().replace(/\s+/g,'');
25252 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25254 if ( cwhite.length && cblack.indexOf(l) > -1) {
25255 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25256 //node.removeAttribute(n);
25260 // only allow 'c whitelisted system attributes'
25261 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25262 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25263 //node.removeAttribute(n);
25273 if (clean.length) {
25274 node.setAttribute(n, clean.join(';'));
25276 node.removeAttribute(n);
25282 for (var i = node.attributes.length-1; i > -1 ; i--) {
25283 var a = node.attributes[i];
25286 if (a.name.toLowerCase().substr(0,2)=='on') {
25287 node.removeAttribute(a.name);
25290 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25291 node.removeAttribute(a.name);
25294 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25295 cleanAttr(a.name,a.value); // fixme..
25298 if (a.name == 'style') {
25299 cleanStyle(a.name,a.value);
25302 /// clean up MS crap..
25303 // tecnically this should be a list of valid class'es..
25306 if (a.name == 'class') {
25307 if (a.value.match(/^Mso/)) {
25308 node.removeAttribute('class');
25311 if (a.value.match(/^body$/)) {
25312 node.removeAttribute('class');
25323 this.cleanUpChildren(node);
25329 * Clean up MS wordisms...
25331 cleanWord : function(node)
25334 this.cleanWord(this.doc.body);
25339 node.nodeName == 'SPAN' &&
25340 !node.hasAttributes() &&
25341 node.childNodes.length == 1 &&
25342 node.firstChild.nodeName == "#text"
25344 var textNode = node.firstChild;
25345 node.removeChild(textNode);
25346 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25347 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25349 node.parentNode.insertBefore(textNode, node);
25350 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25351 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25353 node.parentNode.removeChild(node);
25356 if (node.nodeName == "#text") {
25357 // clean up silly Windows -- stuff?
25360 if (node.nodeName == "#comment") {
25361 node.parentNode.removeChild(node);
25362 // clean up silly Windows -- stuff?
25366 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25367 node.parentNode.removeChild(node);
25370 //Roo.log(node.tagName);
25371 // remove - but keep children..
25372 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25373 //Roo.log('-- removed');
25374 while (node.childNodes.length) {
25375 var cn = node.childNodes[0];
25376 node.removeChild(cn);
25377 node.parentNode.insertBefore(cn, node);
25378 // move node to parent - and clean it..
25379 this.cleanWord(cn);
25381 node.parentNode.removeChild(node);
25382 /// no need to iterate chidlren = it's got none..
25383 //this.iterateChildren(node, this.cleanWord);
25387 if (node.className.length) {
25389 var cn = node.className.split(/\W+/);
25391 Roo.each(cn, function(cls) {
25392 if (cls.match(/Mso[a-zA-Z]+/)) {
25397 node.className = cna.length ? cna.join(' ') : '';
25399 node.removeAttribute("class");
25403 if (node.hasAttribute("lang")) {
25404 node.removeAttribute("lang");
25407 if (node.hasAttribute("style")) {
25409 var styles = node.getAttribute("style").split(";");
25411 Roo.each(styles, function(s) {
25412 if (!s.match(/:/)) {
25415 var kv = s.split(":");
25416 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25419 // what ever is left... we allow.
25422 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25423 if (!nstyle.length) {
25424 node.removeAttribute('style');
25427 this.iterateChildren(node, this.cleanWord);
25433 * iterateChildren of a Node, calling fn each time, using this as the scole..
25434 * @param {DomNode} node node to iterate children of.
25435 * @param {Function} fn method of this class to call on each item.
25437 iterateChildren : function(node, fn)
25439 if (!node.childNodes.length) {
25442 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25443 fn.call(this, node.childNodes[i])
25449 * cleanTableWidths.
25451 * Quite often pasting from word etc.. results in tables with column and widths.
25452 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25455 cleanTableWidths : function(node)
25460 this.cleanTableWidths(this.doc.body);
25465 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25468 Roo.log(node.tagName);
25469 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25470 this.iterateChildren(node, this.cleanTableWidths);
25473 if (node.hasAttribute('width')) {
25474 node.removeAttribute('width');
25478 if (node.hasAttribute("style")) {
25481 var styles = node.getAttribute("style").split(";");
25483 Roo.each(styles, function(s) {
25484 if (!s.match(/:/)) {
25487 var kv = s.split(":");
25488 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25491 // what ever is left... we allow.
25494 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25495 if (!nstyle.length) {
25496 node.removeAttribute('style');
25500 this.iterateChildren(node, this.cleanTableWidths);
25508 domToHTML : function(currentElement, depth, nopadtext) {
25510 depth = depth || 0;
25511 nopadtext = nopadtext || false;
25513 if (!currentElement) {
25514 return this.domToHTML(this.doc.body);
25517 //Roo.log(currentElement);
25519 var allText = false;
25520 var nodeName = currentElement.nodeName;
25521 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25523 if (nodeName == '#text') {
25525 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25530 if (nodeName != 'BODY') {
25533 // Prints the node tagName, such as <A>, <IMG>, etc
25536 for(i = 0; i < currentElement.attributes.length;i++) {
25538 var aname = currentElement.attributes.item(i).name;
25539 if (!currentElement.attributes.item(i).value.length) {
25542 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25545 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25554 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25557 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25562 // Traverse the tree
25564 var currentElementChild = currentElement.childNodes.item(i);
25565 var allText = true;
25566 var innerHTML = '';
25568 while (currentElementChild) {
25569 // Formatting code (indent the tree so it looks nice on the screen)
25570 var nopad = nopadtext;
25571 if (lastnode == 'SPAN') {
25575 if (currentElementChild.nodeName == '#text') {
25576 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25577 toadd = nopadtext ? toadd : toadd.trim();
25578 if (!nopad && toadd.length > 80) {
25579 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25581 innerHTML += toadd;
25584 currentElementChild = currentElement.childNodes.item(i);
25590 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25592 // Recursively traverse the tree structure of the child node
25593 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25594 lastnode = currentElementChild.nodeName;
25596 currentElementChild=currentElement.childNodes.item(i);
25602 // The remaining code is mostly for formatting the tree
25603 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25608 ret+= "</"+tagName+">";
25614 applyBlacklists : function()
25616 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25617 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25621 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25622 if (b.indexOf(tag) > -1) {
25625 this.white.push(tag);
25629 Roo.each(w, function(tag) {
25630 if (b.indexOf(tag) > -1) {
25633 if (this.white.indexOf(tag) > -1) {
25636 this.white.push(tag);
25641 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25642 if (w.indexOf(tag) > -1) {
25645 this.black.push(tag);
25649 Roo.each(b, function(tag) {
25650 if (w.indexOf(tag) > -1) {
25653 if (this.black.indexOf(tag) > -1) {
25656 this.black.push(tag);
25661 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25662 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25666 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25667 if (b.indexOf(tag) > -1) {
25670 this.cwhite.push(tag);
25674 Roo.each(w, function(tag) {
25675 if (b.indexOf(tag) > -1) {
25678 if (this.cwhite.indexOf(tag) > -1) {
25681 this.cwhite.push(tag);
25686 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25687 if (w.indexOf(tag) > -1) {
25690 this.cblack.push(tag);
25694 Roo.each(b, function(tag) {
25695 if (w.indexOf(tag) > -1) {
25698 if (this.cblack.indexOf(tag) > -1) {
25701 this.cblack.push(tag);
25706 setStylesheets : function(stylesheets)
25708 if(typeof(stylesheets) == 'string'){
25709 Roo.get(this.iframe.contentDocument.head).createChild({
25711 rel : 'stylesheet',
25720 Roo.each(stylesheets, function(s) {
25725 Roo.get(_this.iframe.contentDocument.head).createChild({
25727 rel : 'stylesheet',
25736 removeStylesheets : function()
25740 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25745 setStyle : function(style)
25747 Roo.get(this.iframe.contentDocument.head).createChild({
25756 // hide stuff that is not compatible
25770 * @event specialkey
25774 * @cfg {String} fieldClass @hide
25777 * @cfg {String} focusClass @hide
25780 * @cfg {String} autoCreate @hide
25783 * @cfg {String} inputType @hide
25786 * @cfg {String} invalidClass @hide
25789 * @cfg {String} invalidText @hide
25792 * @cfg {String} msgFx @hide
25795 * @cfg {String} validateOnBlur @hide
25799 Roo.HtmlEditorCore.white = [
25800 'area', 'br', 'img', 'input', 'hr', 'wbr',
25802 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25803 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25804 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25805 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25806 'table', 'ul', 'xmp',
25808 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25811 'dir', 'menu', 'ol', 'ul', 'dl',
25817 Roo.HtmlEditorCore.black = [
25818 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25820 'base', 'basefont', 'bgsound', 'blink', 'body',
25821 'frame', 'frameset', 'head', 'html', 'ilayer',
25822 'iframe', 'layer', 'link', 'meta', 'object',
25823 'script', 'style' ,'title', 'xml' // clean later..
25825 Roo.HtmlEditorCore.clean = [
25826 'script', 'style', 'title', 'xml'
25828 Roo.HtmlEditorCore.remove = [
25833 Roo.HtmlEditorCore.ablack = [
25837 Roo.HtmlEditorCore.aclean = [
25838 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25842 Roo.HtmlEditorCore.pwhite= [
25843 'http', 'https', 'mailto'
25846 // white listed style attributes.
25847 Roo.HtmlEditorCore.cwhite= [
25848 // 'text-align', /// default is to allow most things..
25854 // black listed style attributes.
25855 Roo.HtmlEditorCore.cblack= [
25856 // 'font-size' -- this can be set by the project
25860 Roo.HtmlEditorCore.swapCodes =[
25861 [ 8211, "–" ],
25862 [ 8212, "—" ],
25879 * @class Roo.bootstrap.HtmlEditor
25880 * @extends Roo.bootstrap.TextArea
25881 * Bootstrap HtmlEditor class
25884 * Create a new HtmlEditor
25885 * @param {Object} config The config object
25888 Roo.bootstrap.HtmlEditor = function(config){
25889 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25890 if (!this.toolbars) {
25891 this.toolbars = [];
25894 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25897 * @event initialize
25898 * Fires when the editor is fully initialized (including the iframe)
25899 * @param {HtmlEditor} this
25904 * Fires when the editor is first receives the focus. Any insertion must wait
25905 * until after this event.
25906 * @param {HtmlEditor} this
25910 * @event beforesync
25911 * Fires before the textarea is updated with content from the editor iframe. Return false
25912 * to cancel the sync.
25913 * @param {HtmlEditor} this
25914 * @param {String} html
25918 * @event beforepush
25919 * Fires before the iframe editor is updated with content from the textarea. Return false
25920 * to cancel the push.
25921 * @param {HtmlEditor} this
25922 * @param {String} html
25927 * Fires when the textarea is updated with content from the editor iframe.
25928 * @param {HtmlEditor} this
25929 * @param {String} html
25934 * Fires when the iframe editor is updated with content from the textarea.
25935 * @param {HtmlEditor} this
25936 * @param {String} html
25940 * @event editmodechange
25941 * Fires when the editor switches edit modes
25942 * @param {HtmlEditor} this
25943 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25945 editmodechange: true,
25947 * @event editorevent
25948 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25949 * @param {HtmlEditor} this
25953 * @event firstfocus
25954 * Fires when on first focus - needed by toolbars..
25955 * @param {HtmlEditor} this
25960 * Auto save the htmlEditor value as a file into Events
25961 * @param {HtmlEditor} this
25965 * @event savedpreview
25966 * preview the saved version of htmlEditor
25967 * @param {HtmlEditor} this
25974 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25978 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25983 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25988 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25993 * @cfg {Number} height (in pixels)
25997 * @cfg {Number} width (in pixels)
26002 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26005 stylesheets: false,
26010 // private properties
26011 validationEvent : false,
26013 initialized : false,
26016 onFocus : Roo.emptyFn,
26018 hideMode:'offsets',
26020 tbContainer : false,
26024 toolbarContainer :function() {
26025 return this.wrap.select('.x-html-editor-tb',true).first();
26029 * Protected method that will not generally be called directly. It
26030 * is called when the editor creates its toolbar. Override this method if you need to
26031 * add custom toolbar buttons.
26032 * @param {HtmlEditor} editor
26034 createToolbar : function(){
26035 Roo.log('renewing');
26036 Roo.log("create toolbars");
26038 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26039 this.toolbars[0].render(this.toolbarContainer());
26043 // if (!editor.toolbars || !editor.toolbars.length) {
26044 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26047 // for (var i =0 ; i < editor.toolbars.length;i++) {
26048 // editor.toolbars[i] = Roo.factory(
26049 // typeof(editor.toolbars[i]) == 'string' ?
26050 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26051 // Roo.bootstrap.HtmlEditor);
26052 // editor.toolbars[i].init(editor);
26058 onRender : function(ct, position)
26060 // Roo.log("Call onRender: " + this.xtype);
26062 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26064 this.wrap = this.inputEl().wrap({
26065 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26068 this.editorcore.onRender(ct, position);
26070 if (this.resizable) {
26071 this.resizeEl = new Roo.Resizable(this.wrap, {
26075 minHeight : this.height,
26076 height: this.height,
26077 handles : this.resizable,
26080 resize : function(r, w, h) {
26081 _t.onResize(w,h); // -something
26087 this.createToolbar(this);
26090 if(!this.width && this.resizable){
26091 this.setSize(this.wrap.getSize());
26093 if (this.resizeEl) {
26094 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26095 // should trigger onReize..
26101 onResize : function(w, h)
26103 Roo.log('resize: ' +w + ',' + h );
26104 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26108 if(this.inputEl() ){
26109 if(typeof w == 'number'){
26110 var aw = w - this.wrap.getFrameWidth('lr');
26111 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26114 if(typeof h == 'number'){
26115 var tbh = -11; // fixme it needs to tool bar size!
26116 for (var i =0; i < this.toolbars.length;i++) {
26117 // fixme - ask toolbars for heights?
26118 tbh += this.toolbars[i].el.getHeight();
26119 //if (this.toolbars[i].footer) {
26120 // tbh += this.toolbars[i].footer.el.getHeight();
26128 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26129 ah -= 5; // knock a few pixes off for look..
26130 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26134 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26135 this.editorcore.onResize(ew,eh);
26140 * Toggles the editor between standard and source edit mode.
26141 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26143 toggleSourceEdit : function(sourceEditMode)
26145 this.editorcore.toggleSourceEdit(sourceEditMode);
26147 if(this.editorcore.sourceEditMode){
26148 Roo.log('editor - showing textarea');
26151 // Roo.log(this.syncValue());
26153 this.inputEl().removeClass(['hide', 'x-hidden']);
26154 this.inputEl().dom.removeAttribute('tabIndex');
26155 this.inputEl().focus();
26157 Roo.log('editor - hiding textarea');
26159 // Roo.log(this.pushValue());
26162 this.inputEl().addClass(['hide', 'x-hidden']);
26163 this.inputEl().dom.setAttribute('tabIndex', -1);
26164 //this.deferFocus();
26167 if(this.resizable){
26168 this.setSize(this.wrap.getSize());
26171 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26174 // private (for BoxComponent)
26175 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26177 // private (for BoxComponent)
26178 getResizeEl : function(){
26182 // private (for BoxComponent)
26183 getPositionEl : function(){
26188 initEvents : function(){
26189 this.originalValue = this.getValue();
26193 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26196 // markInvalid : Roo.emptyFn,
26198 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26201 // clearInvalid : Roo.emptyFn,
26203 setValue : function(v){
26204 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26205 this.editorcore.pushValue();
26210 deferFocus : function(){
26211 this.focus.defer(10, this);
26215 focus : function(){
26216 this.editorcore.focus();
26222 onDestroy : function(){
26228 for (var i =0; i < this.toolbars.length;i++) {
26229 // fixme - ask toolbars for heights?
26230 this.toolbars[i].onDestroy();
26233 this.wrap.dom.innerHTML = '';
26234 this.wrap.remove();
26239 onFirstFocus : function(){
26240 //Roo.log("onFirstFocus");
26241 this.editorcore.onFirstFocus();
26242 for (var i =0; i < this.toolbars.length;i++) {
26243 this.toolbars[i].onFirstFocus();
26249 syncValue : function()
26251 this.editorcore.syncValue();
26254 pushValue : function()
26256 this.editorcore.pushValue();
26260 // hide stuff that is not compatible
26274 * @event specialkey
26278 * @cfg {String} fieldClass @hide
26281 * @cfg {String} focusClass @hide
26284 * @cfg {String} autoCreate @hide
26287 * @cfg {String} inputType @hide
26291 * @cfg {String} invalidText @hide
26294 * @cfg {String} msgFx @hide
26297 * @cfg {String} validateOnBlur @hide
26306 Roo.namespace('Roo.bootstrap.htmleditor');
26308 * @class Roo.bootstrap.HtmlEditorToolbar1
26314 new Roo.bootstrap.HtmlEditor({
26317 new Roo.bootstrap.HtmlEditorToolbar1({
26318 disable : { fonts: 1 , format: 1, ..., ... , ...],
26324 * @cfg {Object} disable List of elements to disable..
26325 * @cfg {Array} btns List of additional buttons.
26329 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26332 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26335 Roo.apply(this, config);
26337 // default disabled, based on 'good practice'..
26338 this.disable = this.disable || {};
26339 Roo.applyIf(this.disable, {
26342 specialElements : true
26344 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26346 this.editor = config.editor;
26347 this.editorcore = config.editor.editorcore;
26349 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26351 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26352 // dont call parent... till later.
26354 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26359 editorcore : false,
26364 "h1","h2","h3","h4","h5","h6",
26366 "abbr", "acronym", "address", "cite", "samp", "var",
26370 onRender : function(ct, position)
26372 // Roo.log("Call onRender: " + this.xtype);
26374 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26376 this.el.dom.style.marginBottom = '0';
26378 var editorcore = this.editorcore;
26379 var editor= this.editor;
26382 var btn = function(id,cmd , toggle, handler, html){
26384 var event = toggle ? 'toggle' : 'click';
26389 xns: Roo.bootstrap,
26393 enableToggle:toggle !== false,
26395 pressed : toggle ? false : null,
26398 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26399 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26405 // var cb_box = function...
26410 xns: Roo.bootstrap,
26415 xns: Roo.bootstrap,
26419 Roo.each(this.formats, function(f) {
26420 style.menu.items.push({
26422 xns: Roo.bootstrap,
26423 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26428 editorcore.insertTag(this.tagname);
26435 children.push(style);
26437 btn('bold',false,true);
26438 btn('italic',false,true);
26439 btn('align-left', 'justifyleft',true);
26440 btn('align-center', 'justifycenter',true);
26441 btn('align-right' , 'justifyright',true);
26442 btn('link', false, false, function(btn) {
26443 //Roo.log("create link?");
26444 var url = prompt(this.createLinkText, this.defaultLinkValue);
26445 if(url && url != 'http:/'+'/'){
26446 this.editorcore.relayCmd('createlink', url);
26449 btn('list','insertunorderedlist',true);
26450 btn('pencil', false,true, function(btn){
26452 this.toggleSourceEdit(btn.pressed);
26455 if (this.editor.btns.length > 0) {
26456 for (var i = 0; i<this.editor.btns.length; i++) {
26457 children.push(this.editor.btns[i]);
26465 xns: Roo.bootstrap,
26470 xns: Roo.bootstrap,
26475 cog.menu.items.push({
26477 xns: Roo.bootstrap,
26478 html : Clean styles,
26483 editorcore.insertTag(this.tagname);
26492 this.xtype = 'NavSimplebar';
26494 for(var i=0;i< children.length;i++) {
26496 this.buttons.add(this.addxtypeChild(children[i]));
26500 editor.on('editorevent', this.updateToolbar, this);
26502 onBtnClick : function(id)
26504 this.editorcore.relayCmd(id);
26505 this.editorcore.focus();
26509 * Protected method that will not generally be called directly. It triggers
26510 * a toolbar update by reading the markup state of the current selection in the editor.
26512 updateToolbar: function(){
26514 if(!this.editorcore.activated){
26515 this.editor.onFirstFocus(); // is this neeed?
26519 var btns = this.buttons;
26520 var doc = this.editorcore.doc;
26521 btns.get('bold').setActive(doc.queryCommandState('bold'));
26522 btns.get('italic').setActive(doc.queryCommandState('italic'));
26523 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26525 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26526 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26527 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26529 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26530 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26533 var ans = this.editorcore.getAllAncestors();
26534 if (this.formatCombo) {
26537 var store = this.formatCombo.store;
26538 this.formatCombo.setValue("");
26539 for (var i =0; i < ans.length;i++) {
26540 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26542 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26550 // hides menus... - so this cant be on a menu...
26551 Roo.bootstrap.MenuMgr.hideAll();
26553 Roo.bootstrap.MenuMgr.hideAll();
26554 //this.editorsyncValue();
26556 onFirstFocus: function() {
26557 this.buttons.each(function(item){
26561 toggleSourceEdit : function(sourceEditMode){
26564 if(sourceEditMode){
26565 Roo.log("disabling buttons");
26566 this.buttons.each( function(item){
26567 if(item.cmd != 'pencil'){
26573 Roo.log("enabling buttons");
26574 if(this.editorcore.initialized){
26575 this.buttons.each( function(item){
26581 Roo.log("calling toggole on editor");
26582 // tell the editor that it's been pressed..
26583 this.editor.toggleSourceEdit(sourceEditMode);
26597 * @class Roo.bootstrap.Markdown
26598 * @extends Roo.bootstrap.TextArea
26599 * Bootstrap Showdown editable area
26600 * @cfg {string} content
26603 * Create a new Showdown
26606 Roo.bootstrap.Markdown = function(config){
26607 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26611 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26615 initEvents : function()
26618 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26619 this.markdownEl = this.el.createChild({
26620 cls : 'roo-markdown-area'
26622 this.inputEl().addClass('d-none');
26623 if (this.getValue() == '') {
26624 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26627 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26629 this.markdownEl.on('click', this.toggleTextEdit, this);
26630 this.on('blur', this.toggleTextEdit, this);
26631 this.on('specialkey', this.resizeTextArea, this);
26634 toggleTextEdit : function()
26636 var sh = this.markdownEl.getHeight();
26637 this.inputEl().addClass('d-none');
26638 this.markdownEl.addClass('d-none');
26639 if (!this.editing) {
26641 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26642 this.inputEl().removeClass('d-none');
26643 this.inputEl().focus();
26644 this.editing = true;
26647 // show showdown...
26648 this.updateMarkdown();
26649 this.markdownEl.removeClass('d-none');
26650 this.editing = false;
26653 updateMarkdown : function()
26655 if (this.getValue() == '') {
26656 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26660 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26663 resizeTextArea: function () {
26666 Roo.log([sh, this.getValue().split("\n").length * 30]);
26667 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26669 setValue : function(val)
26671 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26672 if (!this.editing) {
26673 this.updateMarkdown();
26679 if (!this.editing) {
26680 this.toggleTextEdit();
26688 * @class Roo.bootstrap.Table.AbstractSelectionModel
26689 * @extends Roo.util.Observable
26690 * Abstract base class for grid SelectionModels. It provides the interface that should be
26691 * implemented by descendant classes. This class should not be directly instantiated.
26694 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26695 this.locked = false;
26696 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26700 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26701 /** @ignore Called by the grid automatically. Do not call directly. */
26702 init : function(grid){
26708 * Locks the selections.
26711 this.locked = true;
26715 * Unlocks the selections.
26717 unlock : function(){
26718 this.locked = false;
26722 * Returns true if the selections are locked.
26723 * @return {Boolean}
26725 isLocked : function(){
26726 return this.locked;
26730 initEvents : function ()
26736 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26737 * @class Roo.bootstrap.Table.RowSelectionModel
26738 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26739 * It supports multiple selections and keyboard selection/navigation.
26741 * @param {Object} config
26744 Roo.bootstrap.Table.RowSelectionModel = function(config){
26745 Roo.apply(this, config);
26746 this.selections = new Roo.util.MixedCollection(false, function(o){
26751 this.lastActive = false;
26755 * @event selectionchange
26756 * Fires when the selection changes
26757 * @param {SelectionModel} this
26759 "selectionchange" : true,
26761 * @event afterselectionchange
26762 * Fires after the selection changes (eg. by key press or clicking)
26763 * @param {SelectionModel} this
26765 "afterselectionchange" : true,
26767 * @event beforerowselect
26768 * Fires when a row is selected being selected, return false to cancel.
26769 * @param {SelectionModel} this
26770 * @param {Number} rowIndex The selected index
26771 * @param {Boolean} keepExisting False if other selections will be cleared
26773 "beforerowselect" : true,
26776 * Fires when a row is selected.
26777 * @param {SelectionModel} this
26778 * @param {Number} rowIndex The selected index
26779 * @param {Roo.data.Record} r The record
26781 "rowselect" : true,
26783 * @event rowdeselect
26784 * Fires when a row is deselected.
26785 * @param {SelectionModel} this
26786 * @param {Number} rowIndex The selected index
26788 "rowdeselect" : true
26790 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26791 this.locked = false;
26794 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26796 * @cfg {Boolean} singleSelect
26797 * True to allow selection of only one row at a time (defaults to false)
26799 singleSelect : false,
26802 initEvents : function()
26805 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26806 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26807 //}else{ // allow click to work like normal
26808 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26810 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26811 this.grid.on("rowclick", this.handleMouseDown, this);
26813 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26814 "up" : function(e){
26816 this.selectPrevious(e.shiftKey);
26817 }else if(this.last !== false && this.lastActive !== false){
26818 var last = this.last;
26819 this.selectRange(this.last, this.lastActive-1);
26820 this.grid.getView().focusRow(this.lastActive);
26821 if(last !== false){
26825 this.selectFirstRow();
26827 this.fireEvent("afterselectionchange", this);
26829 "down" : function(e){
26831 this.selectNext(e.shiftKey);
26832 }else if(this.last !== false && this.lastActive !== false){
26833 var last = this.last;
26834 this.selectRange(this.last, this.lastActive+1);
26835 this.grid.getView().focusRow(this.lastActive);
26836 if(last !== false){
26840 this.selectFirstRow();
26842 this.fireEvent("afterselectionchange", this);
26846 this.grid.store.on('load', function(){
26847 this.selections.clear();
26850 var view = this.grid.view;
26851 view.on("refresh", this.onRefresh, this);
26852 view.on("rowupdated", this.onRowUpdated, this);
26853 view.on("rowremoved", this.onRemove, this);
26858 onRefresh : function()
26860 var ds = this.grid.store, i, v = this.grid.view;
26861 var s = this.selections;
26862 s.each(function(r){
26863 if((i = ds.indexOfId(r.id)) != -1){
26872 onRemove : function(v, index, r){
26873 this.selections.remove(r);
26877 onRowUpdated : function(v, index, r){
26878 if(this.isSelected(r)){
26879 v.onRowSelect(index);
26885 * @param {Array} records The records to select
26886 * @param {Boolean} keepExisting (optional) True to keep existing selections
26888 selectRecords : function(records, keepExisting)
26891 this.clearSelections();
26893 var ds = this.grid.store;
26894 for(var i = 0, len = records.length; i < len; i++){
26895 this.selectRow(ds.indexOf(records[i]), true);
26900 * Gets the number of selected rows.
26903 getCount : function(){
26904 return this.selections.length;
26908 * Selects the first row in the grid.
26910 selectFirstRow : function(){
26915 * Select the last row.
26916 * @param {Boolean} keepExisting (optional) True to keep existing selections
26918 selectLastRow : function(keepExisting){
26919 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26920 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26924 * Selects the row immediately following the last selected row.
26925 * @param {Boolean} keepExisting (optional) True to keep existing selections
26927 selectNext : function(keepExisting)
26929 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26930 this.selectRow(this.last+1, keepExisting);
26931 this.grid.getView().focusRow(this.last);
26936 * Selects the row that precedes the last selected row.
26937 * @param {Boolean} keepExisting (optional) True to keep existing selections
26939 selectPrevious : function(keepExisting){
26941 this.selectRow(this.last-1, keepExisting);
26942 this.grid.getView().focusRow(this.last);
26947 * Returns the selected records
26948 * @return {Array} Array of selected records
26950 getSelections : function(){
26951 return [].concat(this.selections.items);
26955 * Returns the first selected record.
26958 getSelected : function(){
26959 return this.selections.itemAt(0);
26964 * Clears all selections.
26966 clearSelections : function(fast)
26972 var ds = this.grid.store;
26973 var s = this.selections;
26974 s.each(function(r){
26975 this.deselectRow(ds.indexOfId(r.id));
26979 this.selections.clear();
26986 * Selects all rows.
26988 selectAll : function(){
26992 this.selections.clear();
26993 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26994 this.selectRow(i, true);
26999 * Returns True if there is a selection.
27000 * @return {Boolean}
27002 hasSelection : function(){
27003 return this.selections.length > 0;
27007 * Returns True if the specified row is selected.
27008 * @param {Number/Record} record The record or index of the record to check
27009 * @return {Boolean}
27011 isSelected : function(index){
27012 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27013 return (r && this.selections.key(r.id) ? true : false);
27017 * Returns True if the specified record id is selected.
27018 * @param {String} id The id of record to check
27019 * @return {Boolean}
27021 isIdSelected : function(id){
27022 return (this.selections.key(id) ? true : false);
27027 handleMouseDBClick : function(e, t){
27031 handleMouseDown : function(e, t)
27033 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27034 if(this.isLocked() || rowIndex < 0 ){
27037 if(e.shiftKey && this.last !== false){
27038 var last = this.last;
27039 this.selectRange(last, rowIndex, e.ctrlKey);
27040 this.last = last; // reset the last
27044 var isSelected = this.isSelected(rowIndex);
27045 //Roo.log("select row:" + rowIndex);
27047 this.deselectRow(rowIndex);
27049 this.selectRow(rowIndex, true);
27053 if(e.button !== 0 && isSelected){
27054 alert('rowIndex 2: ' + rowIndex);
27055 view.focusRow(rowIndex);
27056 }else if(e.ctrlKey && isSelected){
27057 this.deselectRow(rowIndex);
27058 }else if(!isSelected){
27059 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27060 view.focusRow(rowIndex);
27064 this.fireEvent("afterselectionchange", this);
27067 handleDragableRowClick : function(grid, rowIndex, e)
27069 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27070 this.selectRow(rowIndex, false);
27071 grid.view.focusRow(rowIndex);
27072 this.fireEvent("afterselectionchange", this);
27077 * Selects multiple rows.
27078 * @param {Array} rows Array of the indexes of the row to select
27079 * @param {Boolean} keepExisting (optional) True to keep existing selections
27081 selectRows : function(rows, keepExisting){
27083 this.clearSelections();
27085 for(var i = 0, len = rows.length; i < len; i++){
27086 this.selectRow(rows[i], true);
27091 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27092 * @param {Number} startRow The index of the first row in the range
27093 * @param {Number} endRow The index of the last row in the range
27094 * @param {Boolean} keepExisting (optional) True to retain existing selections
27096 selectRange : function(startRow, endRow, keepExisting){
27101 this.clearSelections();
27103 if(startRow <= endRow){
27104 for(var i = startRow; i <= endRow; i++){
27105 this.selectRow(i, true);
27108 for(var i = startRow; i >= endRow; i--){
27109 this.selectRow(i, true);
27115 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27116 * @param {Number} startRow The index of the first row in the range
27117 * @param {Number} endRow The index of the last row in the range
27119 deselectRange : function(startRow, endRow, preventViewNotify){
27123 for(var i = startRow; i <= endRow; i++){
27124 this.deselectRow(i, preventViewNotify);
27130 * @param {Number} row The index of the row to select
27131 * @param {Boolean} keepExisting (optional) True to keep existing selections
27133 selectRow : function(index, keepExisting, preventViewNotify)
27135 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27138 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27139 if(!keepExisting || this.singleSelect){
27140 this.clearSelections();
27143 var r = this.grid.store.getAt(index);
27144 //console.log('selectRow - record id :' + r.id);
27146 this.selections.add(r);
27147 this.last = this.lastActive = index;
27148 if(!preventViewNotify){
27149 var proxy = new Roo.Element(
27150 this.grid.getRowDom(index)
27152 proxy.addClass('bg-info info');
27154 this.fireEvent("rowselect", this, index, r);
27155 this.fireEvent("selectionchange", this);
27161 * @param {Number} row The index of the row to deselect
27163 deselectRow : function(index, preventViewNotify)
27168 if(this.last == index){
27171 if(this.lastActive == index){
27172 this.lastActive = false;
27175 var r = this.grid.store.getAt(index);
27180 this.selections.remove(r);
27181 //.console.log('deselectRow - record id :' + r.id);
27182 if(!preventViewNotify){
27184 var proxy = new Roo.Element(
27185 this.grid.getRowDom(index)
27187 proxy.removeClass('bg-info info');
27189 this.fireEvent("rowdeselect", this, index);
27190 this.fireEvent("selectionchange", this);
27194 restoreLast : function(){
27196 this.last = this._last;
27201 acceptsNav : function(row, col, cm){
27202 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27206 onEditorKey : function(field, e){
27207 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27212 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27214 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27216 }else if(k == e.ENTER && !e.ctrlKey){
27220 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27222 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27224 }else if(k == e.ESC){
27228 g.startEditing(newCell[0], newCell[1]);
27234 * Ext JS Library 1.1.1
27235 * Copyright(c) 2006-2007, Ext JS, LLC.
27237 * Originally Released Under LGPL - original licence link has changed is not relivant.
27240 * <script type="text/javascript">
27244 * @class Roo.bootstrap.PagingToolbar
27245 * @extends Roo.bootstrap.NavSimplebar
27246 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27248 * Create a new PagingToolbar
27249 * @param {Object} config The config object
27250 * @param {Roo.data.Store} store
27252 Roo.bootstrap.PagingToolbar = function(config)
27254 // old args format still supported... - xtype is prefered..
27255 // created from xtype...
27257 this.ds = config.dataSource;
27259 if (config.store && !this.ds) {
27260 this.store= Roo.factory(config.store, Roo.data);
27261 this.ds = this.store;
27262 this.ds.xmodule = this.xmodule || false;
27265 this.toolbarItems = [];
27266 if (config.items) {
27267 this.toolbarItems = config.items;
27270 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27275 this.bind(this.ds);
27278 if (Roo.bootstrap.version == 4) {
27279 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27281 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27286 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27288 * @cfg {Roo.data.Store} dataSource
27289 * The underlying data store providing the paged data
27292 * @cfg {String/HTMLElement/Element} container
27293 * container The id or element that will contain the toolbar
27296 * @cfg {Boolean} displayInfo
27297 * True to display the displayMsg (defaults to false)
27300 * @cfg {Number} pageSize
27301 * The number of records to display per page (defaults to 20)
27305 * @cfg {String} displayMsg
27306 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27308 displayMsg : 'Displaying {0} - {1} of {2}',
27310 * @cfg {String} emptyMsg
27311 * The message to display when no records are found (defaults to "No data to display")
27313 emptyMsg : 'No data to display',
27315 * Customizable piece of the default paging text (defaults to "Page")
27318 beforePageText : "Page",
27320 * Customizable piece of the default paging text (defaults to "of %0")
27323 afterPageText : "of {0}",
27325 * Customizable piece of the default paging text (defaults to "First Page")
27328 firstText : "First Page",
27330 * Customizable piece of the default paging text (defaults to "Previous Page")
27333 prevText : "Previous Page",
27335 * Customizable piece of the default paging text (defaults to "Next Page")
27338 nextText : "Next Page",
27340 * Customizable piece of the default paging text (defaults to "Last Page")
27343 lastText : "Last Page",
27345 * Customizable piece of the default paging text (defaults to "Refresh")
27348 refreshText : "Refresh",
27352 onRender : function(ct, position)
27354 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27355 this.navgroup.parentId = this.id;
27356 this.navgroup.onRender(this.el, null);
27357 // add the buttons to the navgroup
27359 if(this.displayInfo){
27360 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27361 this.displayEl = this.el.select('.x-paging-info', true).first();
27362 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27363 // this.displayEl = navel.el.select('span',true).first();
27369 Roo.each(_this.buttons, function(e){ // this might need to use render????
27370 Roo.factory(e).render(_this.el);
27374 Roo.each(_this.toolbarItems, function(e) {
27375 _this.navgroup.addItem(e);
27379 this.first = this.navgroup.addItem({
27380 tooltip: this.firstText,
27381 cls: "prev btn-outline-secondary",
27382 html : ' <i class="fa fa-step-backward"></i>',
27384 preventDefault: true,
27385 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27388 this.prev = this.navgroup.addItem({
27389 tooltip: this.prevText,
27390 cls: "prev btn-outline-secondary",
27391 html : ' <i class="fa fa-backward"></i>',
27393 preventDefault: true,
27394 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27396 //this.addSeparator();
27399 var field = this.navgroup.addItem( {
27401 cls : 'x-paging-position btn-outline-secondary',
27403 html : this.beforePageText +
27404 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27405 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27408 this.field = field.el.select('input', true).first();
27409 this.field.on("keydown", this.onPagingKeydown, this);
27410 this.field.on("focus", function(){this.dom.select();});
27413 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27414 //this.field.setHeight(18);
27415 //this.addSeparator();
27416 this.next = this.navgroup.addItem({
27417 tooltip: this.nextText,
27418 cls: "next btn-outline-secondary",
27419 html : ' <i class="fa fa-forward"></i>',
27421 preventDefault: true,
27422 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27424 this.last = this.navgroup.addItem({
27425 tooltip: this.lastText,
27426 html : ' <i class="fa fa-step-forward"></i>',
27427 cls: "next btn-outline-secondary",
27429 preventDefault: true,
27430 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27432 //this.addSeparator();
27433 this.loading = this.navgroup.addItem({
27434 tooltip: this.refreshText,
27435 cls: "btn-outline-secondary",
27436 html : ' <i class="fa fa-refresh"></i>',
27437 preventDefault: true,
27438 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27444 updateInfo : function(){
27445 if(this.displayEl){
27446 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27447 var msg = count == 0 ?
27451 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27453 this.displayEl.update(msg);
27458 onLoad : function(ds, r, o)
27460 this.cursor = o.params && o.params.start ? o.params.start : 0;
27462 var d = this.getPageData(),
27467 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27468 this.field.dom.value = ap;
27469 this.first.setDisabled(ap == 1);
27470 this.prev.setDisabled(ap == 1);
27471 this.next.setDisabled(ap == ps);
27472 this.last.setDisabled(ap == ps);
27473 this.loading.enable();
27478 getPageData : function(){
27479 var total = this.ds.getTotalCount();
27482 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27483 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27488 onLoadError : function(){
27489 this.loading.enable();
27493 onPagingKeydown : function(e){
27494 var k = e.getKey();
27495 var d = this.getPageData();
27497 var v = this.field.dom.value, pageNum;
27498 if(!v || isNaN(pageNum = parseInt(v, 10))){
27499 this.field.dom.value = d.activePage;
27502 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27503 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27506 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))
27508 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27509 this.field.dom.value = pageNum;
27510 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27513 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27515 var v = this.field.dom.value, pageNum;
27516 var increment = (e.shiftKey) ? 10 : 1;
27517 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27520 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27521 this.field.dom.value = d.activePage;
27524 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27526 this.field.dom.value = parseInt(v, 10) + increment;
27527 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27528 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27535 beforeLoad : function(){
27537 this.loading.disable();
27542 onClick : function(which){
27551 ds.load({params:{start: 0, limit: this.pageSize}});
27554 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27557 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27560 var total = ds.getTotalCount();
27561 var extra = total % this.pageSize;
27562 var lastStart = extra ? (total - extra) : total-this.pageSize;
27563 ds.load({params:{start: lastStart, limit: this.pageSize}});
27566 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27572 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27573 * @param {Roo.data.Store} store The data store to unbind
27575 unbind : function(ds){
27576 ds.un("beforeload", this.beforeLoad, this);
27577 ds.un("load", this.onLoad, this);
27578 ds.un("loadexception", this.onLoadError, this);
27579 ds.un("remove", this.updateInfo, this);
27580 ds.un("add", this.updateInfo, this);
27581 this.ds = undefined;
27585 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27586 * @param {Roo.data.Store} store The data store to bind
27588 bind : function(ds){
27589 ds.on("beforeload", this.beforeLoad, this);
27590 ds.on("load", this.onLoad, this);
27591 ds.on("loadexception", this.onLoadError, this);
27592 ds.on("remove", this.updateInfo, this);
27593 ds.on("add", this.updateInfo, this);
27604 * @class Roo.bootstrap.MessageBar
27605 * @extends Roo.bootstrap.Component
27606 * Bootstrap MessageBar class
27607 * @cfg {String} html contents of the MessageBar
27608 * @cfg {String} weight (info | success | warning | danger) default info
27609 * @cfg {String} beforeClass insert the bar before the given class
27610 * @cfg {Boolean} closable (true | false) default false
27611 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27614 * Create a new Element
27615 * @param {Object} config The config object
27618 Roo.bootstrap.MessageBar = function(config){
27619 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27622 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27628 beforeClass: 'bootstrap-sticky-wrap',
27630 getAutoCreate : function(){
27634 cls: 'alert alert-dismissable alert-' + this.weight,
27639 html: this.html || ''
27645 cfg.cls += ' alert-messages-fixed';
27659 onRender : function(ct, position)
27661 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27664 var cfg = Roo.apply({}, this.getAutoCreate());
27668 cfg.cls += ' ' + this.cls;
27671 cfg.style = this.style;
27673 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27675 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27678 this.el.select('>button.close').on('click', this.hide, this);
27684 if (!this.rendered) {
27690 this.fireEvent('show', this);
27696 if (!this.rendered) {
27702 this.fireEvent('hide', this);
27705 update : function()
27707 // var e = this.el.dom.firstChild;
27709 // if(this.closable){
27710 // e = e.nextSibling;
27713 // e.data = this.html || '';
27715 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27731 * @class Roo.bootstrap.Graph
27732 * @extends Roo.bootstrap.Component
27733 * Bootstrap Graph class
27737 @cfg {String} graphtype bar | vbar | pie
27738 @cfg {number} g_x coodinator | centre x (pie)
27739 @cfg {number} g_y coodinator | centre y (pie)
27740 @cfg {number} g_r radius (pie)
27741 @cfg {number} g_height height of the chart (respected by all elements in the set)
27742 @cfg {number} g_width width of the chart (respected by all elements in the set)
27743 @cfg {Object} title The title of the chart
27746 -opts (object) options for the chart
27748 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27749 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27751 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.
27752 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27754 o stretch (boolean)
27756 -opts (object) options for the pie
27759 o startAngle (number)
27760 o endAngle (number)
27764 * Create a new Input
27765 * @param {Object} config The config object
27768 Roo.bootstrap.Graph = function(config){
27769 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27775 * The img click event for the img.
27776 * @param {Roo.EventObject} e
27782 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27793 //g_colors: this.colors,
27800 getAutoCreate : function(){
27811 onRender : function(ct,position){
27814 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27816 if (typeof(Raphael) == 'undefined') {
27817 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27821 this.raphael = Raphael(this.el.dom);
27823 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27824 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27825 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27826 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27828 r.text(160, 10, "Single Series Chart").attr(txtattr);
27829 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27830 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27831 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27833 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27834 r.barchart(330, 10, 300, 220, data1);
27835 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27836 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27839 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27840 // r.barchart(30, 30, 560, 250, xdata, {
27841 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27842 // axis : "0 0 1 1",
27843 // axisxlabels : xdata
27844 // //yvalues : cols,
27847 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27849 // this.load(null,xdata,{
27850 // axis : "0 0 1 1",
27851 // axisxlabels : xdata
27856 load : function(graphtype,xdata,opts)
27858 this.raphael.clear();
27860 graphtype = this.graphtype;
27865 var r = this.raphael,
27866 fin = function () {
27867 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27869 fout = function () {
27870 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27872 pfin = function() {
27873 this.sector.stop();
27874 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27877 this.label[0].stop();
27878 this.label[0].attr({ r: 7.5 });
27879 this.label[1].attr({ "font-weight": 800 });
27882 pfout = function() {
27883 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27886 this.label[0].animate({ r: 5 }, 500, "bounce");
27887 this.label[1].attr({ "font-weight": 400 });
27893 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27896 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27899 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27900 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27902 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27909 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27914 setTitle: function(o)
27919 initEvents: function() {
27922 this.el.on('click', this.onClick, this);
27926 onClick : function(e)
27928 Roo.log('img onclick');
27929 this.fireEvent('click', this, e);
27941 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27944 * @class Roo.bootstrap.dash.NumberBox
27945 * @extends Roo.bootstrap.Component
27946 * Bootstrap NumberBox class
27947 * @cfg {String} headline Box headline
27948 * @cfg {String} content Box content
27949 * @cfg {String} icon Box icon
27950 * @cfg {String} footer Footer text
27951 * @cfg {String} fhref Footer href
27954 * Create a new NumberBox
27955 * @param {Object} config The config object
27959 Roo.bootstrap.dash.NumberBox = function(config){
27960 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27964 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27973 getAutoCreate : function(){
27977 cls : 'small-box ',
27985 cls : 'roo-headline',
27986 html : this.headline
27990 cls : 'roo-content',
27991 html : this.content
28005 cls : 'ion ' + this.icon
28014 cls : 'small-box-footer',
28015 href : this.fhref || '#',
28019 cfg.cn.push(footer);
28026 onRender : function(ct,position){
28027 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28034 setHeadline: function (value)
28036 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28039 setFooter: function (value, href)
28041 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28044 this.el.select('a.small-box-footer',true).first().attr('href', href);
28049 setContent: function (value)
28051 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28054 initEvents: function()
28068 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28071 * @class Roo.bootstrap.dash.TabBox
28072 * @extends Roo.bootstrap.Component
28073 * Bootstrap TabBox class
28074 * @cfg {String} title Title of the TabBox
28075 * @cfg {String} icon Icon of the TabBox
28076 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28077 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28080 * Create a new TabBox
28081 * @param {Object} config The config object
28085 Roo.bootstrap.dash.TabBox = function(config){
28086 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28091 * When a pane is added
28092 * @param {Roo.bootstrap.dash.TabPane} pane
28096 * @event activatepane
28097 * When a pane is activated
28098 * @param {Roo.bootstrap.dash.TabPane} pane
28100 "activatepane" : true
28108 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28113 tabScrollable : false,
28115 getChildContainer : function()
28117 return this.el.select('.tab-content', true).first();
28120 getAutoCreate : function(){
28124 cls: 'pull-left header',
28132 cls: 'fa ' + this.icon
28138 cls: 'nav nav-tabs pull-right',
28144 if(this.tabScrollable){
28151 cls: 'nav nav-tabs pull-right',
28162 cls: 'nav-tabs-custom',
28167 cls: 'tab-content no-padding',
28175 initEvents : function()
28177 //Roo.log('add add pane handler');
28178 this.on('addpane', this.onAddPane, this);
28181 * Updates the box title
28182 * @param {String} html to set the title to.
28184 setTitle : function(value)
28186 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28188 onAddPane : function(pane)
28190 this.panes.push(pane);
28191 //Roo.log('addpane');
28193 // tabs are rendere left to right..
28194 if(!this.showtabs){
28198 var ctr = this.el.select('.nav-tabs', true).first();
28201 var existing = ctr.select('.nav-tab',true);
28202 var qty = existing.getCount();;
28205 var tab = ctr.createChild({
28207 cls : 'nav-tab' + (qty ? '' : ' active'),
28215 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28218 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28220 pane.el.addClass('active');
28225 onTabClick : function(ev,un,ob,pane)
28227 //Roo.log('tab - prev default');
28228 ev.preventDefault();
28231 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28232 pane.tab.addClass('active');
28233 //Roo.log(pane.title);
28234 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28235 // technically we should have a deactivate event.. but maybe add later.
28236 // and it should not de-activate the selected tab...
28237 this.fireEvent('activatepane', pane);
28238 pane.el.addClass('active');
28239 pane.fireEvent('activate');
28244 getActivePane : function()
28247 Roo.each(this.panes, function(p) {
28248 if(p.el.hasClass('active')){
28269 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28271 * @class Roo.bootstrap.TabPane
28272 * @extends Roo.bootstrap.Component
28273 * Bootstrap TabPane class
28274 * @cfg {Boolean} active (false | true) Default false
28275 * @cfg {String} title title of panel
28279 * Create a new TabPane
28280 * @param {Object} config The config object
28283 Roo.bootstrap.dash.TabPane = function(config){
28284 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28290 * When a pane is activated
28291 * @param {Roo.bootstrap.dash.TabPane} pane
28298 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28303 // the tabBox that this is attached to.
28306 getAutoCreate : function()
28314 cfg.cls += ' active';
28319 initEvents : function()
28321 //Roo.log('trigger add pane handler');
28322 this.parent().fireEvent('addpane', this)
28326 * Updates the tab title
28327 * @param {String} html to set the title to.
28329 setTitle: function(str)
28335 this.tab.select('a', true).first().dom.innerHTML = str;
28352 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28355 * @class Roo.bootstrap.menu.Menu
28356 * @extends Roo.bootstrap.Component
28357 * Bootstrap Menu class - container for Menu
28358 * @cfg {String} html Text of the menu
28359 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28360 * @cfg {String} icon Font awesome icon
28361 * @cfg {String} pos Menu align to (top | bottom) default bottom
28365 * Create a new Menu
28366 * @param {Object} config The config object
28370 Roo.bootstrap.menu.Menu = function(config){
28371 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28375 * @event beforeshow
28376 * Fires before this menu is displayed
28377 * @param {Roo.bootstrap.menu.Menu} this
28381 * @event beforehide
28382 * Fires before this menu is hidden
28383 * @param {Roo.bootstrap.menu.Menu} this
28388 * Fires after this menu is displayed
28389 * @param {Roo.bootstrap.menu.Menu} this
28394 * Fires after this menu is hidden
28395 * @param {Roo.bootstrap.menu.Menu} this
28400 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28401 * @param {Roo.bootstrap.menu.Menu} this
28402 * @param {Roo.EventObject} e
28409 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28413 weight : 'default',
28418 getChildContainer : function() {
28419 if(this.isSubMenu){
28423 return this.el.select('ul.dropdown-menu', true).first();
28426 getAutoCreate : function()
28431 cls : 'roo-menu-text',
28439 cls : 'fa ' + this.icon
28450 cls : 'dropdown-button btn btn-' + this.weight,
28455 cls : 'dropdown-toggle btn btn-' + this.weight,
28465 cls : 'dropdown-menu'
28471 if(this.pos == 'top'){
28472 cfg.cls += ' dropup';
28475 if(this.isSubMenu){
28478 cls : 'dropdown-menu'
28485 onRender : function(ct, position)
28487 this.isSubMenu = ct.hasClass('dropdown-submenu');
28489 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28492 initEvents : function()
28494 if(this.isSubMenu){
28498 this.hidden = true;
28500 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28501 this.triggerEl.on('click', this.onTriggerPress, this);
28503 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28504 this.buttonEl.on('click', this.onClick, this);
28510 if(this.isSubMenu){
28514 return this.el.select('ul.dropdown-menu', true).first();
28517 onClick : function(e)
28519 this.fireEvent("click", this, e);
28522 onTriggerPress : function(e)
28524 if (this.isVisible()) {
28531 isVisible : function(){
28532 return !this.hidden;
28537 this.fireEvent("beforeshow", this);
28539 this.hidden = false;
28540 this.el.addClass('open');
28542 Roo.get(document).on("mouseup", this.onMouseUp, this);
28544 this.fireEvent("show", this);
28551 this.fireEvent("beforehide", this);
28553 this.hidden = true;
28554 this.el.removeClass('open');
28556 Roo.get(document).un("mouseup", this.onMouseUp);
28558 this.fireEvent("hide", this);
28561 onMouseUp : function()
28575 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28578 * @class Roo.bootstrap.menu.Item
28579 * @extends Roo.bootstrap.Component
28580 * Bootstrap MenuItem class
28581 * @cfg {Boolean} submenu (true | false) default false
28582 * @cfg {String} html text of the item
28583 * @cfg {String} href the link
28584 * @cfg {Boolean} disable (true | false) default false
28585 * @cfg {Boolean} preventDefault (true | false) default true
28586 * @cfg {String} icon Font awesome icon
28587 * @cfg {String} pos Submenu align to (left | right) default right
28591 * Create a new Item
28592 * @param {Object} config The config object
28596 Roo.bootstrap.menu.Item = function(config){
28597 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28601 * Fires when the mouse is hovering over this menu
28602 * @param {Roo.bootstrap.menu.Item} this
28603 * @param {Roo.EventObject} e
28608 * Fires when the mouse exits this menu
28609 * @param {Roo.bootstrap.menu.Item} this
28610 * @param {Roo.EventObject} e
28616 * The raw click event for the entire grid.
28617 * @param {Roo.EventObject} e
28623 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28628 preventDefault: true,
28633 getAutoCreate : function()
28638 cls : 'roo-menu-item-text',
28646 cls : 'fa ' + this.icon
28655 href : this.href || '#',
28662 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28666 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28668 if(this.pos == 'left'){
28669 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28676 initEvents : function()
28678 this.el.on('mouseover', this.onMouseOver, this);
28679 this.el.on('mouseout', this.onMouseOut, this);
28681 this.el.select('a', true).first().on('click', this.onClick, this);
28685 onClick : function(e)
28687 if(this.preventDefault){
28688 e.preventDefault();
28691 this.fireEvent("click", this, e);
28694 onMouseOver : function(e)
28696 if(this.submenu && this.pos == 'left'){
28697 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28700 this.fireEvent("mouseover", this, e);
28703 onMouseOut : function(e)
28705 this.fireEvent("mouseout", this, e);
28717 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28720 * @class Roo.bootstrap.menu.Separator
28721 * @extends Roo.bootstrap.Component
28722 * Bootstrap Separator class
28725 * Create a new Separator
28726 * @param {Object} config The config object
28730 Roo.bootstrap.menu.Separator = function(config){
28731 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28734 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28736 getAutoCreate : function(){
28757 * @class Roo.bootstrap.Tooltip
28758 * Bootstrap Tooltip class
28759 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28760 * to determine which dom element triggers the tooltip.
28762 * It needs to add support for additional attributes like tooltip-position
28765 * Create a new Toolti
28766 * @param {Object} config The config object
28769 Roo.bootstrap.Tooltip = function(config){
28770 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28772 this.alignment = Roo.bootstrap.Tooltip.alignment;
28774 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28775 this.alignment = config.alignment;
28780 Roo.apply(Roo.bootstrap.Tooltip, {
28782 * @function init initialize tooltip monitoring.
28786 currentTip : false,
28787 currentRegion : false,
28793 Roo.get(document).on('mouseover', this.enter ,this);
28794 Roo.get(document).on('mouseout', this.leave, this);
28797 this.currentTip = new Roo.bootstrap.Tooltip();
28800 enter : function(ev)
28802 var dom = ev.getTarget();
28804 //Roo.log(['enter',dom]);
28805 var el = Roo.fly(dom);
28806 if (this.currentEl) {
28808 //Roo.log(this.currentEl);
28809 //Roo.log(this.currentEl.contains(dom));
28810 if (this.currentEl == el) {
28813 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28819 if (this.currentTip.el) {
28820 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28824 if(!el || el.dom == document){
28830 // you can not look for children, as if el is the body.. then everythign is the child..
28831 if (!el.attr('tooltip')) { //
28832 if (!el.select("[tooltip]").elements.length) {
28835 // is the mouse over this child...?
28836 bindEl = el.select("[tooltip]").first();
28837 var xy = ev.getXY();
28838 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28839 //Roo.log("not in region.");
28842 //Roo.log("child element over..");
28845 this.currentEl = bindEl;
28846 this.currentTip.bind(bindEl);
28847 this.currentRegion = Roo.lib.Region.getRegion(dom);
28848 this.currentTip.enter();
28851 leave : function(ev)
28853 var dom = ev.getTarget();
28854 //Roo.log(['leave',dom]);
28855 if (!this.currentEl) {
28860 if (dom != this.currentEl.dom) {
28863 var xy = ev.getXY();
28864 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28867 // only activate leave if mouse cursor is outside... bounding box..
28872 if (this.currentTip) {
28873 this.currentTip.leave();
28875 //Roo.log('clear currentEl');
28876 this.currentEl = false;
28881 'left' : ['r-l', [-2,0], 'right'],
28882 'right' : ['l-r', [2,0], 'left'],
28883 'bottom' : ['t-b', [0,2], 'top'],
28884 'top' : [ 'b-t', [0,-2], 'bottom']
28890 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28895 delay : null, // can be { show : 300 , hide: 500}
28899 hoverState : null, //???
28901 placement : 'bottom',
28905 getAutoCreate : function(){
28912 cls : 'tooltip-arrow arrow'
28915 cls : 'tooltip-inner'
28922 bind : function(el)
28927 initEvents : function()
28929 this.arrowEl = this.el.select('.arrow', true).first();
28930 this.innerEl = this.el.select('.tooltip-inner', true).first();
28933 enter : function () {
28935 if (this.timeout != null) {
28936 clearTimeout(this.timeout);
28939 this.hoverState = 'in';
28940 //Roo.log("enter - show");
28941 if (!this.delay || !this.delay.show) {
28946 this.timeout = setTimeout(function () {
28947 if (_t.hoverState == 'in') {
28950 }, this.delay.show);
28954 clearTimeout(this.timeout);
28956 this.hoverState = 'out';
28957 if (!this.delay || !this.delay.hide) {
28963 this.timeout = setTimeout(function () {
28964 //Roo.log("leave - timeout");
28966 if (_t.hoverState == 'out') {
28968 Roo.bootstrap.Tooltip.currentEl = false;
28973 show : function (msg)
28976 this.render(document.body);
28979 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28981 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28983 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28985 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28986 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28988 var placement = typeof this.placement == 'function' ?
28989 this.placement.call(this, this.el, on_el) :
28992 var autoToken = /\s?auto?\s?/i;
28993 var autoPlace = autoToken.test(placement);
28995 placement = placement.replace(autoToken, '') || 'top';
28999 //this.el.setXY([0,0]);
29001 //this.el.dom.style.display='block';
29003 //this.el.appendTo(on_el);
29005 var p = this.getPosition();
29006 var box = this.el.getBox();
29012 var align = this.alignment[placement];
29014 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29016 if(placement == 'top' || placement == 'bottom'){
29018 placement = 'right';
29021 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29022 placement = 'left';
29025 var scroll = Roo.select('body', true).first().getScroll();
29027 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29031 align = this.alignment[placement];
29033 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29037 this.el.alignTo(this.bindEl, align[0],align[1]);
29038 //var arrow = this.el.select('.arrow',true).first();
29039 //arrow.set(align[2],
29041 this.el.addClass(placement);
29042 this.el.addClass("bs-tooltip-"+ placement);
29044 this.el.addClass('in fade show');
29046 this.hoverState = null;
29048 if (this.el.hasClass('fade')) {
29063 //this.el.setXY([0,0]);
29064 this.el.removeClass(['show', 'in']);
29080 * @class Roo.bootstrap.LocationPicker
29081 * @extends Roo.bootstrap.Component
29082 * Bootstrap LocationPicker class
29083 * @cfg {Number} latitude Position when init default 0
29084 * @cfg {Number} longitude Position when init default 0
29085 * @cfg {Number} zoom default 15
29086 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29087 * @cfg {Boolean} mapTypeControl default false
29088 * @cfg {Boolean} disableDoubleClickZoom default false
29089 * @cfg {Boolean} scrollwheel default true
29090 * @cfg {Boolean} streetViewControl default false
29091 * @cfg {Number} radius default 0
29092 * @cfg {String} locationName
29093 * @cfg {Boolean} draggable default true
29094 * @cfg {Boolean} enableAutocomplete default false
29095 * @cfg {Boolean} enableReverseGeocode default true
29096 * @cfg {String} markerTitle
29099 * Create a new LocationPicker
29100 * @param {Object} config The config object
29104 Roo.bootstrap.LocationPicker = function(config){
29106 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29111 * Fires when the picker initialized.
29112 * @param {Roo.bootstrap.LocationPicker} this
29113 * @param {Google Location} location
29117 * @event positionchanged
29118 * Fires when the picker position changed.
29119 * @param {Roo.bootstrap.LocationPicker} this
29120 * @param {Google Location} location
29122 positionchanged : true,
29125 * Fires when the map resize.
29126 * @param {Roo.bootstrap.LocationPicker} this
29131 * Fires when the map show.
29132 * @param {Roo.bootstrap.LocationPicker} this
29137 * Fires when the map hide.
29138 * @param {Roo.bootstrap.LocationPicker} this
29143 * Fires when click the map.
29144 * @param {Roo.bootstrap.LocationPicker} this
29145 * @param {Map event} e
29149 * @event mapRightClick
29150 * Fires when right click the map.
29151 * @param {Roo.bootstrap.LocationPicker} this
29152 * @param {Map event} e
29154 mapRightClick : true,
29156 * @event markerClick
29157 * Fires when click the marker.
29158 * @param {Roo.bootstrap.LocationPicker} this
29159 * @param {Map event} e
29161 markerClick : true,
29163 * @event markerRightClick
29164 * Fires when right click the marker.
29165 * @param {Roo.bootstrap.LocationPicker} this
29166 * @param {Map event} e
29168 markerRightClick : true,
29170 * @event OverlayViewDraw
29171 * Fires when OverlayView Draw
29172 * @param {Roo.bootstrap.LocationPicker} this
29174 OverlayViewDraw : true,
29176 * @event OverlayViewOnAdd
29177 * Fires when OverlayView Draw
29178 * @param {Roo.bootstrap.LocationPicker} this
29180 OverlayViewOnAdd : true,
29182 * @event OverlayViewOnRemove
29183 * Fires when OverlayView Draw
29184 * @param {Roo.bootstrap.LocationPicker} this
29186 OverlayViewOnRemove : true,
29188 * @event OverlayViewShow
29189 * Fires when OverlayView Draw
29190 * @param {Roo.bootstrap.LocationPicker} this
29191 * @param {Pixel} cpx
29193 OverlayViewShow : true,
29195 * @event OverlayViewHide
29196 * Fires when OverlayView Draw
29197 * @param {Roo.bootstrap.LocationPicker} this
29199 OverlayViewHide : true,
29201 * @event loadexception
29202 * Fires when load google lib failed.
29203 * @param {Roo.bootstrap.LocationPicker} this
29205 loadexception : true
29210 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29212 gMapContext: false,
29218 mapTypeControl: false,
29219 disableDoubleClickZoom: false,
29221 streetViewControl: false,
29225 enableAutocomplete: false,
29226 enableReverseGeocode: true,
29229 getAutoCreate: function()
29234 cls: 'roo-location-picker'
29240 initEvents: function(ct, position)
29242 if(!this.el.getWidth() || this.isApplied()){
29246 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29251 initial: function()
29253 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29254 this.fireEvent('loadexception', this);
29258 if(!this.mapTypeId){
29259 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29262 this.gMapContext = this.GMapContext();
29264 this.initOverlayView();
29266 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29270 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29271 _this.setPosition(_this.gMapContext.marker.position);
29274 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29275 _this.fireEvent('mapClick', this, event);
29279 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29280 _this.fireEvent('mapRightClick', this, event);
29284 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29285 _this.fireEvent('markerClick', this, event);
29289 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29290 _this.fireEvent('markerRightClick', this, event);
29294 this.setPosition(this.gMapContext.location);
29296 this.fireEvent('initial', this, this.gMapContext.location);
29299 initOverlayView: function()
29303 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29307 _this.fireEvent('OverlayViewDraw', _this);
29312 _this.fireEvent('OverlayViewOnAdd', _this);
29315 onRemove: function()
29317 _this.fireEvent('OverlayViewOnRemove', _this);
29320 show: function(cpx)
29322 _this.fireEvent('OverlayViewShow', _this, cpx);
29327 _this.fireEvent('OverlayViewHide', _this);
29333 fromLatLngToContainerPixel: function(event)
29335 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29338 isApplied: function()
29340 return this.getGmapContext() == false ? false : true;
29343 getGmapContext: function()
29345 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29348 GMapContext: function()
29350 var position = new google.maps.LatLng(this.latitude, this.longitude);
29352 var _map = new google.maps.Map(this.el.dom, {
29355 mapTypeId: this.mapTypeId,
29356 mapTypeControl: this.mapTypeControl,
29357 disableDoubleClickZoom: this.disableDoubleClickZoom,
29358 scrollwheel: this.scrollwheel,
29359 streetViewControl: this.streetViewControl,
29360 locationName: this.locationName,
29361 draggable: this.draggable,
29362 enableAutocomplete: this.enableAutocomplete,
29363 enableReverseGeocode: this.enableReverseGeocode
29366 var _marker = new google.maps.Marker({
29367 position: position,
29369 title: this.markerTitle,
29370 draggable: this.draggable
29377 location: position,
29378 radius: this.radius,
29379 locationName: this.locationName,
29380 addressComponents: {
29381 formatted_address: null,
29382 addressLine1: null,
29383 addressLine2: null,
29385 streetNumber: null,
29389 stateOrProvince: null
29392 domContainer: this.el.dom,
29393 geodecoder: new google.maps.Geocoder()
29397 drawCircle: function(center, radius, options)
29399 if (this.gMapContext.circle != null) {
29400 this.gMapContext.circle.setMap(null);
29404 options = Roo.apply({}, options, {
29405 strokeColor: "#0000FF",
29406 strokeOpacity: .35,
29408 fillColor: "#0000FF",
29412 options.map = this.gMapContext.map;
29413 options.radius = radius;
29414 options.center = center;
29415 this.gMapContext.circle = new google.maps.Circle(options);
29416 return this.gMapContext.circle;
29422 setPosition: function(location)
29424 this.gMapContext.location = location;
29425 this.gMapContext.marker.setPosition(location);
29426 this.gMapContext.map.panTo(location);
29427 this.drawCircle(location, this.gMapContext.radius, {});
29431 if (this.gMapContext.settings.enableReverseGeocode) {
29432 this.gMapContext.geodecoder.geocode({
29433 latLng: this.gMapContext.location
29434 }, function(results, status) {
29436 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29437 _this.gMapContext.locationName = results[0].formatted_address;
29438 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29440 _this.fireEvent('positionchanged', this, location);
29447 this.fireEvent('positionchanged', this, location);
29452 google.maps.event.trigger(this.gMapContext.map, "resize");
29454 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29456 this.fireEvent('resize', this);
29459 setPositionByLatLng: function(latitude, longitude)
29461 this.setPosition(new google.maps.LatLng(latitude, longitude));
29464 getCurrentPosition: function()
29467 latitude: this.gMapContext.location.lat(),
29468 longitude: this.gMapContext.location.lng()
29472 getAddressName: function()
29474 return this.gMapContext.locationName;
29477 getAddressComponents: function()
29479 return this.gMapContext.addressComponents;
29482 address_component_from_google_geocode: function(address_components)
29486 for (var i = 0; i < address_components.length; i++) {
29487 var component = address_components[i];
29488 if (component.types.indexOf("postal_code") >= 0) {
29489 result.postalCode = component.short_name;
29490 } else if (component.types.indexOf("street_number") >= 0) {
29491 result.streetNumber = component.short_name;
29492 } else if (component.types.indexOf("route") >= 0) {
29493 result.streetName = component.short_name;
29494 } else if (component.types.indexOf("neighborhood") >= 0) {
29495 result.city = component.short_name;
29496 } else if (component.types.indexOf("locality") >= 0) {
29497 result.city = component.short_name;
29498 } else if (component.types.indexOf("sublocality") >= 0) {
29499 result.district = component.short_name;
29500 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29501 result.stateOrProvince = component.short_name;
29502 } else if (component.types.indexOf("country") >= 0) {
29503 result.country = component.short_name;
29507 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29508 result.addressLine2 = "";
29512 setZoomLevel: function(zoom)
29514 this.gMapContext.map.setZoom(zoom);
29527 this.fireEvent('show', this);
29538 this.fireEvent('hide', this);
29543 Roo.apply(Roo.bootstrap.LocationPicker, {
29545 OverlayView : function(map, options)
29547 options = options || {};
29554 * @class Roo.bootstrap.Alert
29555 * @extends Roo.bootstrap.Component
29556 * Bootstrap Alert class - shows an alert area box
29558 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29559 Enter a valid email address
29562 * @cfg {String} title The title of alert
29563 * @cfg {String} html The content of alert
29564 * @cfg {String} weight ( success | info | warning | danger )
29565 * @cfg {String} faicon font-awesomeicon
29568 * Create a new alert
29569 * @param {Object} config The config object
29573 Roo.bootstrap.Alert = function(config){
29574 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29578 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29585 getAutoCreate : function()
29594 cls : 'roo-alert-icon'
29599 cls : 'roo-alert-title',
29604 cls : 'roo-alert-text',
29611 cfg.cn[0].cls += ' fa ' + this.faicon;
29615 cfg.cls += ' alert-' + this.weight;
29621 initEvents: function()
29623 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29626 setTitle : function(str)
29628 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29631 setText : function(str)
29633 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29636 setWeight : function(weight)
29639 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29642 this.weight = weight;
29644 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29647 setIcon : function(icon)
29650 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29653 this.faicon = icon;
29655 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29676 * @class Roo.bootstrap.UploadCropbox
29677 * @extends Roo.bootstrap.Component
29678 * Bootstrap UploadCropbox class
29679 * @cfg {String} emptyText show when image has been loaded
29680 * @cfg {String} rotateNotify show when image too small to rotate
29681 * @cfg {Number} errorTimeout default 3000
29682 * @cfg {Number} minWidth default 300
29683 * @cfg {Number} minHeight default 300
29684 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29685 * @cfg {Boolean} isDocument (true|false) default false
29686 * @cfg {String} url action url
29687 * @cfg {String} paramName default 'imageUpload'
29688 * @cfg {String} method default POST
29689 * @cfg {Boolean} loadMask (true|false) default true
29690 * @cfg {Boolean} loadingText default 'Loading...'
29693 * Create a new UploadCropbox
29694 * @param {Object} config The config object
29697 Roo.bootstrap.UploadCropbox = function(config){
29698 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29702 * @event beforeselectfile
29703 * Fire before select file
29704 * @param {Roo.bootstrap.UploadCropbox} this
29706 "beforeselectfile" : true,
29709 * Fire after initEvent
29710 * @param {Roo.bootstrap.UploadCropbox} this
29715 * Fire after initEvent
29716 * @param {Roo.bootstrap.UploadCropbox} this
29717 * @param {String} data
29722 * Fire when preparing the file data
29723 * @param {Roo.bootstrap.UploadCropbox} this
29724 * @param {Object} file
29729 * Fire when get exception
29730 * @param {Roo.bootstrap.UploadCropbox} this
29731 * @param {XMLHttpRequest} xhr
29733 "exception" : true,
29735 * @event beforeloadcanvas
29736 * Fire before load the canvas
29737 * @param {Roo.bootstrap.UploadCropbox} this
29738 * @param {String} src
29740 "beforeloadcanvas" : true,
29743 * Fire when trash image
29744 * @param {Roo.bootstrap.UploadCropbox} this
29749 * Fire when download the image
29750 * @param {Roo.bootstrap.UploadCropbox} this
29754 * @event footerbuttonclick
29755 * Fire when footerbuttonclick
29756 * @param {Roo.bootstrap.UploadCropbox} this
29757 * @param {String} type
29759 "footerbuttonclick" : true,
29763 * @param {Roo.bootstrap.UploadCropbox} this
29768 * Fire when rotate the image
29769 * @param {Roo.bootstrap.UploadCropbox} this
29770 * @param {String} pos
29775 * Fire when inspect the file
29776 * @param {Roo.bootstrap.UploadCropbox} this
29777 * @param {Object} file
29782 * Fire when xhr upload the file
29783 * @param {Roo.bootstrap.UploadCropbox} this
29784 * @param {Object} data
29789 * Fire when arrange the file data
29790 * @param {Roo.bootstrap.UploadCropbox} this
29791 * @param {Object} formData
29796 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29799 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29801 emptyText : 'Click to upload image',
29802 rotateNotify : 'Image is too small to rotate',
29803 errorTimeout : 3000,
29817 cropType : 'image/jpeg',
29819 canvasLoaded : false,
29820 isDocument : false,
29822 paramName : 'imageUpload',
29824 loadingText : 'Loading...',
29827 getAutoCreate : function()
29831 cls : 'roo-upload-cropbox',
29835 cls : 'roo-upload-cropbox-selector',
29840 cls : 'roo-upload-cropbox-body',
29841 style : 'cursor:pointer',
29845 cls : 'roo-upload-cropbox-preview'
29849 cls : 'roo-upload-cropbox-thumb'
29853 cls : 'roo-upload-cropbox-empty-notify',
29854 html : this.emptyText
29858 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29859 html : this.rotateNotify
29865 cls : 'roo-upload-cropbox-footer',
29868 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29878 onRender : function(ct, position)
29880 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29882 if (this.buttons.length) {
29884 Roo.each(this.buttons, function(bb) {
29886 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29888 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29894 this.maskEl = this.el;
29898 initEvents : function()
29900 this.urlAPI = (window.createObjectURL && window) ||
29901 (window.URL && URL.revokeObjectURL && URL) ||
29902 (window.webkitURL && webkitURL);
29904 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29905 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29907 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29908 this.selectorEl.hide();
29910 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29911 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29913 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29914 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29915 this.thumbEl.hide();
29917 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29918 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29920 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29921 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29922 this.errorEl.hide();
29924 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29925 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29926 this.footerEl.hide();
29928 this.setThumbBoxSize();
29934 this.fireEvent('initial', this);
29941 window.addEventListener("resize", function() { _this.resize(); } );
29943 this.bodyEl.on('click', this.beforeSelectFile, this);
29946 this.bodyEl.on('touchstart', this.onTouchStart, this);
29947 this.bodyEl.on('touchmove', this.onTouchMove, this);
29948 this.bodyEl.on('touchend', this.onTouchEnd, this);
29952 this.bodyEl.on('mousedown', this.onMouseDown, this);
29953 this.bodyEl.on('mousemove', this.onMouseMove, this);
29954 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29955 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29956 Roo.get(document).on('mouseup', this.onMouseUp, this);
29959 this.selectorEl.on('change', this.onFileSelected, this);
29965 this.baseScale = 1;
29967 this.baseRotate = 1;
29968 this.dragable = false;
29969 this.pinching = false;
29972 this.cropData = false;
29973 this.notifyEl.dom.innerHTML = this.emptyText;
29975 this.selectorEl.dom.value = '';
29979 resize : function()
29981 if(this.fireEvent('resize', this) != false){
29982 this.setThumbBoxPosition();
29983 this.setCanvasPosition();
29987 onFooterButtonClick : function(e, el, o, type)
29990 case 'rotate-left' :
29991 this.onRotateLeft(e);
29993 case 'rotate-right' :
29994 this.onRotateRight(e);
29997 this.beforeSelectFile(e);
30012 this.fireEvent('footerbuttonclick', this, type);
30015 beforeSelectFile : function(e)
30017 e.preventDefault();
30019 if(this.fireEvent('beforeselectfile', this) != false){
30020 this.selectorEl.dom.click();
30024 onFileSelected : function(e)
30026 e.preventDefault();
30028 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30032 var file = this.selectorEl.dom.files[0];
30034 if(this.fireEvent('inspect', this, file) != false){
30035 this.prepare(file);
30040 trash : function(e)
30042 this.fireEvent('trash', this);
30045 download : function(e)
30047 this.fireEvent('download', this);
30050 loadCanvas : function(src)
30052 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30056 this.imageEl = document.createElement('img');
30060 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30062 this.imageEl.src = src;
30066 onLoadCanvas : function()
30068 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30069 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30071 this.bodyEl.un('click', this.beforeSelectFile, this);
30073 this.notifyEl.hide();
30074 this.thumbEl.show();
30075 this.footerEl.show();
30077 this.baseRotateLevel();
30079 if(this.isDocument){
30080 this.setThumbBoxSize();
30083 this.setThumbBoxPosition();
30085 this.baseScaleLevel();
30091 this.canvasLoaded = true;
30094 this.maskEl.unmask();
30099 setCanvasPosition : function()
30101 if(!this.canvasEl){
30105 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30106 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30108 this.previewEl.setLeft(pw);
30109 this.previewEl.setTop(ph);
30113 onMouseDown : function(e)
30117 this.dragable = true;
30118 this.pinching = false;
30120 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30121 this.dragable = false;
30125 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30126 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30130 onMouseMove : function(e)
30134 if(!this.canvasLoaded){
30138 if (!this.dragable){
30142 var minX = Math.ceil(this.thumbEl.getLeft(true));
30143 var minY = Math.ceil(this.thumbEl.getTop(true));
30145 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30146 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30148 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30149 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30151 x = x - this.mouseX;
30152 y = y - this.mouseY;
30154 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30155 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30157 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30158 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30160 this.previewEl.setLeft(bgX);
30161 this.previewEl.setTop(bgY);
30163 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30164 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30167 onMouseUp : function(e)
30171 this.dragable = false;
30174 onMouseWheel : function(e)
30178 this.startScale = this.scale;
30180 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30182 if(!this.zoomable()){
30183 this.scale = this.startScale;
30192 zoomable : function()
30194 var minScale = this.thumbEl.getWidth() / this.minWidth;
30196 if(this.minWidth < this.minHeight){
30197 minScale = this.thumbEl.getHeight() / this.minHeight;
30200 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30201 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30205 (this.rotate == 0 || this.rotate == 180) &&
30207 width > this.imageEl.OriginWidth ||
30208 height > this.imageEl.OriginHeight ||
30209 (width < this.minWidth && height < this.minHeight)
30217 (this.rotate == 90 || this.rotate == 270) &&
30219 width > this.imageEl.OriginWidth ||
30220 height > this.imageEl.OriginHeight ||
30221 (width < this.minHeight && height < this.minWidth)
30228 !this.isDocument &&
30229 (this.rotate == 0 || this.rotate == 180) &&
30231 width < this.minWidth ||
30232 width > this.imageEl.OriginWidth ||
30233 height < this.minHeight ||
30234 height > this.imageEl.OriginHeight
30241 !this.isDocument &&
30242 (this.rotate == 90 || this.rotate == 270) &&
30244 width < this.minHeight ||
30245 width > this.imageEl.OriginWidth ||
30246 height < this.minWidth ||
30247 height > this.imageEl.OriginHeight
30257 onRotateLeft : function(e)
30259 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30261 var minScale = this.thumbEl.getWidth() / this.minWidth;
30263 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30264 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30266 this.startScale = this.scale;
30268 while (this.getScaleLevel() < minScale){
30270 this.scale = this.scale + 1;
30272 if(!this.zoomable()){
30277 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30278 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30283 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30290 this.scale = this.startScale;
30292 this.onRotateFail();
30297 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30299 if(this.isDocument){
30300 this.setThumbBoxSize();
30301 this.setThumbBoxPosition();
30302 this.setCanvasPosition();
30307 this.fireEvent('rotate', this, 'left');
30311 onRotateRight : function(e)
30313 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30315 var minScale = this.thumbEl.getWidth() / this.minWidth;
30317 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30318 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30320 this.startScale = this.scale;
30322 while (this.getScaleLevel() < minScale){
30324 this.scale = this.scale + 1;
30326 if(!this.zoomable()){
30331 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30332 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30337 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30344 this.scale = this.startScale;
30346 this.onRotateFail();
30351 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30353 if(this.isDocument){
30354 this.setThumbBoxSize();
30355 this.setThumbBoxPosition();
30356 this.setCanvasPosition();
30361 this.fireEvent('rotate', this, 'right');
30364 onRotateFail : function()
30366 this.errorEl.show(true);
30370 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30375 this.previewEl.dom.innerHTML = '';
30377 var canvasEl = document.createElement("canvas");
30379 var contextEl = canvasEl.getContext("2d");
30381 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30382 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30383 var center = this.imageEl.OriginWidth / 2;
30385 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30386 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30387 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30388 center = this.imageEl.OriginHeight / 2;
30391 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30393 contextEl.translate(center, center);
30394 contextEl.rotate(this.rotate * Math.PI / 180);
30396 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30398 this.canvasEl = document.createElement("canvas");
30400 this.contextEl = this.canvasEl.getContext("2d");
30402 switch (this.rotate) {
30405 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30406 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30408 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30413 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30414 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30416 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30417 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);
30421 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30426 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30427 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30429 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30430 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);
30434 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);
30439 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30440 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30442 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30443 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30447 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);
30454 this.previewEl.appendChild(this.canvasEl);
30456 this.setCanvasPosition();
30461 if(!this.canvasLoaded){
30465 var imageCanvas = document.createElement("canvas");
30467 var imageContext = imageCanvas.getContext("2d");
30469 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30470 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30472 var center = imageCanvas.width / 2;
30474 imageContext.translate(center, center);
30476 imageContext.rotate(this.rotate * Math.PI / 180);
30478 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30480 var canvas = document.createElement("canvas");
30482 var context = canvas.getContext("2d");
30484 canvas.width = this.minWidth;
30485 canvas.height = this.minHeight;
30487 switch (this.rotate) {
30490 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30491 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30493 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30494 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30496 var targetWidth = this.minWidth - 2 * x;
30497 var targetHeight = this.minHeight - 2 * y;
30501 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30502 scale = targetWidth / width;
30505 if(x > 0 && y == 0){
30506 scale = targetHeight / height;
30509 if(x > 0 && y > 0){
30510 scale = targetWidth / width;
30512 if(width < height){
30513 scale = targetHeight / height;
30517 context.scale(scale, scale);
30519 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30520 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30522 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30523 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30525 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30530 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30531 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30533 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30534 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30536 var targetWidth = this.minWidth - 2 * x;
30537 var targetHeight = this.minHeight - 2 * y;
30541 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30542 scale = targetWidth / width;
30545 if(x > 0 && y == 0){
30546 scale = targetHeight / height;
30549 if(x > 0 && y > 0){
30550 scale = targetWidth / width;
30552 if(width < height){
30553 scale = targetHeight / height;
30557 context.scale(scale, scale);
30559 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30560 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30562 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30563 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30565 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30567 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30572 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30573 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30575 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30576 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30578 var targetWidth = this.minWidth - 2 * x;
30579 var targetHeight = this.minHeight - 2 * y;
30583 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30584 scale = targetWidth / width;
30587 if(x > 0 && y == 0){
30588 scale = targetHeight / height;
30591 if(x > 0 && y > 0){
30592 scale = targetWidth / width;
30594 if(width < height){
30595 scale = targetHeight / height;
30599 context.scale(scale, scale);
30601 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30602 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30604 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30605 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30607 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30608 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30610 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30615 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30616 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30618 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30619 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30621 var targetWidth = this.minWidth - 2 * x;
30622 var targetHeight = this.minHeight - 2 * y;
30626 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30627 scale = targetWidth / width;
30630 if(x > 0 && y == 0){
30631 scale = targetHeight / height;
30634 if(x > 0 && y > 0){
30635 scale = targetWidth / width;
30637 if(width < height){
30638 scale = targetHeight / height;
30642 context.scale(scale, scale);
30644 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30645 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30647 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30648 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30650 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30652 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30659 this.cropData = canvas.toDataURL(this.cropType);
30661 if(this.fireEvent('crop', this, this.cropData) !== false){
30662 this.process(this.file, this.cropData);
30669 setThumbBoxSize : function()
30673 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30674 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30675 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30677 this.minWidth = width;
30678 this.minHeight = height;
30680 if(this.rotate == 90 || this.rotate == 270){
30681 this.minWidth = height;
30682 this.minHeight = width;
30687 width = Math.ceil(this.minWidth * height / this.minHeight);
30689 if(this.minWidth > this.minHeight){
30691 height = Math.ceil(this.minHeight * width / this.minWidth);
30694 this.thumbEl.setStyle({
30695 width : width + 'px',
30696 height : height + 'px'
30703 setThumbBoxPosition : function()
30705 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30706 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30708 this.thumbEl.setLeft(x);
30709 this.thumbEl.setTop(y);
30713 baseRotateLevel : function()
30715 this.baseRotate = 1;
30718 typeof(this.exif) != 'undefined' &&
30719 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30720 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30722 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30725 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30729 baseScaleLevel : function()
30733 if(this.isDocument){
30735 if(this.baseRotate == 6 || this.baseRotate == 8){
30737 height = this.thumbEl.getHeight();
30738 this.baseScale = height / this.imageEl.OriginWidth;
30740 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30741 width = this.thumbEl.getWidth();
30742 this.baseScale = width / this.imageEl.OriginHeight;
30748 height = this.thumbEl.getHeight();
30749 this.baseScale = height / this.imageEl.OriginHeight;
30751 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30752 width = this.thumbEl.getWidth();
30753 this.baseScale = width / this.imageEl.OriginWidth;
30759 if(this.baseRotate == 6 || this.baseRotate == 8){
30761 width = this.thumbEl.getHeight();
30762 this.baseScale = width / this.imageEl.OriginHeight;
30764 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30765 height = this.thumbEl.getWidth();
30766 this.baseScale = height / this.imageEl.OriginHeight;
30769 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30770 height = this.thumbEl.getWidth();
30771 this.baseScale = height / this.imageEl.OriginHeight;
30773 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30774 width = this.thumbEl.getHeight();
30775 this.baseScale = width / this.imageEl.OriginWidth;
30782 width = this.thumbEl.getWidth();
30783 this.baseScale = width / this.imageEl.OriginWidth;
30785 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30786 height = this.thumbEl.getHeight();
30787 this.baseScale = height / this.imageEl.OriginHeight;
30790 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30792 height = this.thumbEl.getHeight();
30793 this.baseScale = height / this.imageEl.OriginHeight;
30795 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30796 width = this.thumbEl.getWidth();
30797 this.baseScale = width / this.imageEl.OriginWidth;
30805 getScaleLevel : function()
30807 return this.baseScale * Math.pow(1.1, this.scale);
30810 onTouchStart : function(e)
30812 if(!this.canvasLoaded){
30813 this.beforeSelectFile(e);
30817 var touches = e.browserEvent.touches;
30823 if(touches.length == 1){
30824 this.onMouseDown(e);
30828 if(touches.length != 2){
30834 for(var i = 0, finger; finger = touches[i]; i++){
30835 coords.push(finger.pageX, finger.pageY);
30838 var x = Math.pow(coords[0] - coords[2], 2);
30839 var y = Math.pow(coords[1] - coords[3], 2);
30841 this.startDistance = Math.sqrt(x + y);
30843 this.startScale = this.scale;
30845 this.pinching = true;
30846 this.dragable = false;
30850 onTouchMove : function(e)
30852 if(!this.pinching && !this.dragable){
30856 var touches = e.browserEvent.touches;
30863 this.onMouseMove(e);
30869 for(var i = 0, finger; finger = touches[i]; i++){
30870 coords.push(finger.pageX, finger.pageY);
30873 var x = Math.pow(coords[0] - coords[2], 2);
30874 var y = Math.pow(coords[1] - coords[3], 2);
30876 this.endDistance = Math.sqrt(x + y);
30878 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30880 if(!this.zoomable()){
30881 this.scale = this.startScale;
30889 onTouchEnd : function(e)
30891 this.pinching = false;
30892 this.dragable = false;
30896 process : function(file, crop)
30899 this.maskEl.mask(this.loadingText);
30902 this.xhr = new XMLHttpRequest();
30904 file.xhr = this.xhr;
30906 this.xhr.open(this.method, this.url, true);
30909 "Accept": "application/json",
30910 "Cache-Control": "no-cache",
30911 "X-Requested-With": "XMLHttpRequest"
30914 for (var headerName in headers) {
30915 var headerValue = headers[headerName];
30917 this.xhr.setRequestHeader(headerName, headerValue);
30923 this.xhr.onload = function()
30925 _this.xhrOnLoad(_this.xhr);
30928 this.xhr.onerror = function()
30930 _this.xhrOnError(_this.xhr);
30933 var formData = new FormData();
30935 formData.append('returnHTML', 'NO');
30938 formData.append('crop', crop);
30941 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30942 formData.append(this.paramName, file, file.name);
30945 if(typeof(file.filename) != 'undefined'){
30946 formData.append('filename', file.filename);
30949 if(typeof(file.mimetype) != 'undefined'){
30950 formData.append('mimetype', file.mimetype);
30953 if(this.fireEvent('arrange', this, formData) != false){
30954 this.xhr.send(formData);
30958 xhrOnLoad : function(xhr)
30961 this.maskEl.unmask();
30964 if (xhr.readyState !== 4) {
30965 this.fireEvent('exception', this, xhr);
30969 var response = Roo.decode(xhr.responseText);
30971 if(!response.success){
30972 this.fireEvent('exception', this, xhr);
30976 var response = Roo.decode(xhr.responseText);
30978 this.fireEvent('upload', this, response);
30982 xhrOnError : function()
30985 this.maskEl.unmask();
30988 Roo.log('xhr on error');
30990 var response = Roo.decode(xhr.responseText);
30996 prepare : function(file)
30999 this.maskEl.mask(this.loadingText);
31005 if(typeof(file) === 'string'){
31006 this.loadCanvas(file);
31010 if(!file || !this.urlAPI){
31015 this.cropType = file.type;
31019 if(this.fireEvent('prepare', this, this.file) != false){
31021 var reader = new FileReader();
31023 reader.onload = function (e) {
31024 if (e.target.error) {
31025 Roo.log(e.target.error);
31029 var buffer = e.target.result,
31030 dataView = new DataView(buffer),
31032 maxOffset = dataView.byteLength - 4,
31036 if (dataView.getUint16(0) === 0xffd8) {
31037 while (offset < maxOffset) {
31038 markerBytes = dataView.getUint16(offset);
31040 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31041 markerLength = dataView.getUint16(offset + 2) + 2;
31042 if (offset + markerLength > dataView.byteLength) {
31043 Roo.log('Invalid meta data: Invalid segment size.');
31047 if(markerBytes == 0xffe1){
31048 _this.parseExifData(
31055 offset += markerLength;
31065 var url = _this.urlAPI.createObjectURL(_this.file);
31067 _this.loadCanvas(url);
31072 reader.readAsArrayBuffer(this.file);
31078 parseExifData : function(dataView, offset, length)
31080 var tiffOffset = offset + 10,
31084 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31085 // No Exif data, might be XMP data instead
31089 // Check for the ASCII code for "Exif" (0x45786966):
31090 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31091 // No Exif data, might be XMP data instead
31094 if (tiffOffset + 8 > dataView.byteLength) {
31095 Roo.log('Invalid Exif data: Invalid segment size.');
31098 // Check for the two null bytes:
31099 if (dataView.getUint16(offset + 8) !== 0x0000) {
31100 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31103 // Check the byte alignment:
31104 switch (dataView.getUint16(tiffOffset)) {
31106 littleEndian = true;
31109 littleEndian = false;
31112 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31115 // Check for the TIFF tag marker (0x002A):
31116 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31117 Roo.log('Invalid Exif data: Missing TIFF marker.');
31120 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31121 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31123 this.parseExifTags(
31126 tiffOffset + dirOffset,
31131 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31136 if (dirOffset + 6 > dataView.byteLength) {
31137 Roo.log('Invalid Exif data: Invalid directory offset.');
31140 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31141 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31142 if (dirEndOffset + 4 > dataView.byteLength) {
31143 Roo.log('Invalid Exif data: Invalid directory size.');
31146 for (i = 0; i < tagsNumber; i += 1) {
31150 dirOffset + 2 + 12 * i, // tag offset
31154 // Return the offset to the next directory:
31155 return dataView.getUint32(dirEndOffset, littleEndian);
31158 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31160 var tag = dataView.getUint16(offset, littleEndian);
31162 this.exif[tag] = this.getExifValue(
31166 dataView.getUint16(offset + 2, littleEndian), // tag type
31167 dataView.getUint32(offset + 4, littleEndian), // tag length
31172 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31174 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31183 Roo.log('Invalid Exif data: Invalid tag type.');
31187 tagSize = tagType.size * length;
31188 // Determine if the value is contained in the dataOffset bytes,
31189 // or if the value at the dataOffset is a pointer to the actual data:
31190 dataOffset = tagSize > 4 ?
31191 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31192 if (dataOffset + tagSize > dataView.byteLength) {
31193 Roo.log('Invalid Exif data: Invalid data offset.');
31196 if (length === 1) {
31197 return tagType.getValue(dataView, dataOffset, littleEndian);
31200 for (i = 0; i < length; i += 1) {
31201 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31204 if (tagType.ascii) {
31206 // Concatenate the chars:
31207 for (i = 0; i < values.length; i += 1) {
31209 // Ignore the terminating NULL byte(s):
31210 if (c === '\u0000') {
31222 Roo.apply(Roo.bootstrap.UploadCropbox, {
31224 'Orientation': 0x0112
31228 1: 0, //'top-left',
31230 3: 180, //'bottom-right',
31231 // 4: 'bottom-left',
31233 6: 90, //'right-top',
31234 // 7: 'right-bottom',
31235 8: 270 //'left-bottom'
31239 // byte, 8-bit unsigned int:
31241 getValue: function (dataView, dataOffset) {
31242 return dataView.getUint8(dataOffset);
31246 // ascii, 8-bit byte:
31248 getValue: function (dataView, dataOffset) {
31249 return String.fromCharCode(dataView.getUint8(dataOffset));
31254 // short, 16 bit int:
31256 getValue: function (dataView, dataOffset, littleEndian) {
31257 return dataView.getUint16(dataOffset, littleEndian);
31261 // long, 32 bit int:
31263 getValue: function (dataView, dataOffset, littleEndian) {
31264 return dataView.getUint32(dataOffset, littleEndian);
31268 // rational = two long values, first is numerator, second is denominator:
31270 getValue: function (dataView, dataOffset, littleEndian) {
31271 return dataView.getUint32(dataOffset, littleEndian) /
31272 dataView.getUint32(dataOffset + 4, littleEndian);
31276 // slong, 32 bit signed int:
31278 getValue: function (dataView, dataOffset, littleEndian) {
31279 return dataView.getInt32(dataOffset, littleEndian);
31283 // srational, two slongs, first is numerator, second is denominator:
31285 getValue: function (dataView, dataOffset, littleEndian) {
31286 return dataView.getInt32(dataOffset, littleEndian) /
31287 dataView.getInt32(dataOffset + 4, littleEndian);
31297 cls : 'btn-group roo-upload-cropbox-rotate-left',
31298 action : 'rotate-left',
31302 cls : 'btn btn-default',
31303 html : '<i class="fa fa-undo"></i>'
31309 cls : 'btn-group roo-upload-cropbox-picture',
31310 action : 'picture',
31314 cls : 'btn btn-default',
31315 html : '<i class="fa fa-picture-o"></i>'
31321 cls : 'btn-group roo-upload-cropbox-rotate-right',
31322 action : 'rotate-right',
31326 cls : 'btn btn-default',
31327 html : '<i class="fa fa-repeat"></i>'
31335 cls : 'btn-group roo-upload-cropbox-rotate-left',
31336 action : 'rotate-left',
31340 cls : 'btn btn-default',
31341 html : '<i class="fa fa-undo"></i>'
31347 cls : 'btn-group roo-upload-cropbox-download',
31348 action : 'download',
31352 cls : 'btn btn-default',
31353 html : '<i class="fa fa-download"></i>'
31359 cls : 'btn-group roo-upload-cropbox-crop',
31364 cls : 'btn btn-default',
31365 html : '<i class="fa fa-crop"></i>'
31371 cls : 'btn-group roo-upload-cropbox-trash',
31376 cls : 'btn btn-default',
31377 html : '<i class="fa fa-trash"></i>'
31383 cls : 'btn-group roo-upload-cropbox-rotate-right',
31384 action : 'rotate-right',
31388 cls : 'btn btn-default',
31389 html : '<i class="fa fa-repeat"></i>'
31397 cls : 'btn-group roo-upload-cropbox-rotate-left',
31398 action : 'rotate-left',
31402 cls : 'btn btn-default',
31403 html : '<i class="fa fa-undo"></i>'
31409 cls : 'btn-group roo-upload-cropbox-rotate-right',
31410 action : 'rotate-right',
31414 cls : 'btn btn-default',
31415 html : '<i class="fa fa-repeat"></i>'
31428 * @class Roo.bootstrap.DocumentManager
31429 * @extends Roo.bootstrap.Component
31430 * Bootstrap DocumentManager class
31431 * @cfg {String} paramName default 'imageUpload'
31432 * @cfg {String} toolTipName default 'filename'
31433 * @cfg {String} method default POST
31434 * @cfg {String} url action url
31435 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31436 * @cfg {Boolean} multiple multiple upload default true
31437 * @cfg {Number} thumbSize default 300
31438 * @cfg {String} fieldLabel
31439 * @cfg {Number} labelWidth default 4
31440 * @cfg {String} labelAlign (left|top) default left
31441 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31442 * @cfg {Number} labellg set the width of label (1-12)
31443 * @cfg {Number} labelmd set the width of label (1-12)
31444 * @cfg {Number} labelsm set the width of label (1-12)
31445 * @cfg {Number} labelxs set the width of label (1-12)
31448 * Create a new DocumentManager
31449 * @param {Object} config The config object
31452 Roo.bootstrap.DocumentManager = function(config){
31453 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31456 this.delegates = [];
31461 * Fire when initial the DocumentManager
31462 * @param {Roo.bootstrap.DocumentManager} this
31467 * inspect selected file
31468 * @param {Roo.bootstrap.DocumentManager} this
31469 * @param {File} file
31474 * Fire when xhr load exception
31475 * @param {Roo.bootstrap.DocumentManager} this
31476 * @param {XMLHttpRequest} xhr
31478 "exception" : true,
31480 * @event afterupload
31481 * Fire when xhr load exception
31482 * @param {Roo.bootstrap.DocumentManager} this
31483 * @param {XMLHttpRequest} xhr
31485 "afterupload" : true,
31488 * prepare the form data
31489 * @param {Roo.bootstrap.DocumentManager} this
31490 * @param {Object} formData
31495 * Fire when remove the file
31496 * @param {Roo.bootstrap.DocumentManager} this
31497 * @param {Object} file
31502 * Fire after refresh the file
31503 * @param {Roo.bootstrap.DocumentManager} this
31508 * Fire after click the image
31509 * @param {Roo.bootstrap.DocumentManager} this
31510 * @param {Object} file
31515 * Fire when upload a image and editable set to true
31516 * @param {Roo.bootstrap.DocumentManager} this
31517 * @param {Object} file
31521 * @event beforeselectfile
31522 * Fire before select file
31523 * @param {Roo.bootstrap.DocumentManager} this
31525 "beforeselectfile" : true,
31528 * Fire before process file
31529 * @param {Roo.bootstrap.DocumentManager} this
31530 * @param {Object} file
31534 * @event previewrendered
31535 * Fire when preview rendered
31536 * @param {Roo.bootstrap.DocumentManager} this
31537 * @param {Object} file
31539 "previewrendered" : true,
31542 "previewResize" : true
31547 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31556 paramName : 'imageUpload',
31557 toolTipName : 'filename',
31560 labelAlign : 'left',
31570 getAutoCreate : function()
31572 var managerWidget = {
31574 cls : 'roo-document-manager',
31578 cls : 'roo-document-manager-selector',
31583 cls : 'roo-document-manager-uploader',
31587 cls : 'roo-document-manager-upload-btn',
31588 html : '<i class="fa fa-plus"></i>'
31599 cls : 'column col-md-12',
31604 if(this.fieldLabel.length){
31609 cls : 'column col-md-12',
31610 html : this.fieldLabel
31614 cls : 'column col-md-12',
31619 if(this.labelAlign == 'left'){
31624 html : this.fieldLabel
31633 if(this.labelWidth > 12){
31634 content[0].style = "width: " + this.labelWidth + 'px';
31637 if(this.labelWidth < 13 && this.labelmd == 0){
31638 this.labelmd = this.labelWidth;
31641 if(this.labellg > 0){
31642 content[0].cls += ' col-lg-' + this.labellg;
31643 content[1].cls += ' col-lg-' + (12 - this.labellg);
31646 if(this.labelmd > 0){
31647 content[0].cls += ' col-md-' + this.labelmd;
31648 content[1].cls += ' col-md-' + (12 - this.labelmd);
31651 if(this.labelsm > 0){
31652 content[0].cls += ' col-sm-' + this.labelsm;
31653 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31656 if(this.labelxs > 0){
31657 content[0].cls += ' col-xs-' + this.labelxs;
31658 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31666 cls : 'row clearfix',
31674 initEvents : function()
31676 this.managerEl = this.el.select('.roo-document-manager', true).first();
31677 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31679 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31680 this.selectorEl.hide();
31683 this.selectorEl.attr('multiple', 'multiple');
31686 this.selectorEl.on('change', this.onFileSelected, this);
31688 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31689 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31691 this.uploader.on('click', this.onUploaderClick, this);
31693 this.renderProgressDialog();
31697 window.addEventListener("resize", function() { _this.refresh(); } );
31699 this.fireEvent('initial', this);
31702 renderProgressDialog : function()
31706 this.progressDialog = new Roo.bootstrap.Modal({
31707 cls : 'roo-document-manager-progress-dialog',
31708 allow_close : false,
31719 btnclick : function() {
31720 _this.uploadCancel();
31726 this.progressDialog.render(Roo.get(document.body));
31728 this.progress = new Roo.bootstrap.Progress({
31729 cls : 'roo-document-manager-progress',
31734 this.progress.render(this.progressDialog.getChildContainer());
31736 this.progressBar = new Roo.bootstrap.ProgressBar({
31737 cls : 'roo-document-manager-progress-bar',
31740 aria_valuemax : 12,
31744 this.progressBar.render(this.progress.getChildContainer());
31747 onUploaderClick : function(e)
31749 e.preventDefault();
31751 if(this.fireEvent('beforeselectfile', this) != false){
31752 this.selectorEl.dom.click();
31757 onFileSelected : function(e)
31759 e.preventDefault();
31761 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31765 Roo.each(this.selectorEl.dom.files, function(file){
31766 if(this.fireEvent('inspect', this, file) != false){
31767 this.files.push(file);
31777 this.selectorEl.dom.value = '';
31779 if(!this.files || !this.files.length){
31783 if(this.boxes > 0 && this.files.length > this.boxes){
31784 this.files = this.files.slice(0, this.boxes);
31787 this.uploader.show();
31789 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31790 this.uploader.hide();
31799 Roo.each(this.files, function(file){
31801 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31802 var f = this.renderPreview(file);
31807 if(file.type.indexOf('image') != -1){
31808 this.delegates.push(
31810 _this.process(file);
31811 }).createDelegate(this)
31819 _this.process(file);
31820 }).createDelegate(this)
31825 this.files = files;
31827 this.delegates = this.delegates.concat(docs);
31829 if(!this.delegates.length){
31834 this.progressBar.aria_valuemax = this.delegates.length;
31841 arrange : function()
31843 if(!this.delegates.length){
31844 this.progressDialog.hide();
31849 var delegate = this.delegates.shift();
31851 this.progressDialog.show();
31853 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31855 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31860 refresh : function()
31862 this.uploader.show();
31864 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31865 this.uploader.hide();
31868 Roo.isTouch ? this.closable(false) : this.closable(true);
31870 this.fireEvent('refresh', this);
31873 onRemove : function(e, el, o)
31875 e.preventDefault();
31877 this.fireEvent('remove', this, o);
31881 remove : function(o)
31885 Roo.each(this.files, function(file){
31886 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31895 this.files = files;
31902 Roo.each(this.files, function(file){
31907 file.target.remove();
31916 onClick : function(e, el, o)
31918 e.preventDefault();
31920 this.fireEvent('click', this, o);
31924 closable : function(closable)
31926 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31928 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31940 xhrOnLoad : function(xhr)
31942 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31946 if (xhr.readyState !== 4) {
31948 this.fireEvent('exception', this, xhr);
31952 var response = Roo.decode(xhr.responseText);
31954 if(!response.success){
31956 this.fireEvent('exception', this, xhr);
31960 var file = this.renderPreview(response.data);
31962 this.files.push(file);
31966 this.fireEvent('afterupload', this, xhr);
31970 xhrOnError : function(xhr)
31972 Roo.log('xhr on error');
31974 var response = Roo.decode(xhr.responseText);
31981 process : function(file)
31983 if(this.fireEvent('process', this, file) !== false){
31984 if(this.editable && file.type.indexOf('image') != -1){
31985 this.fireEvent('edit', this, file);
31989 this.uploadStart(file, false);
31996 uploadStart : function(file, crop)
31998 this.xhr = new XMLHttpRequest();
32000 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32005 file.xhr = this.xhr;
32007 this.managerEl.createChild({
32009 cls : 'roo-document-manager-loading',
32013 tooltip : file.name,
32014 cls : 'roo-document-manager-thumb',
32015 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32021 this.xhr.open(this.method, this.url, true);
32024 "Accept": "application/json",
32025 "Cache-Control": "no-cache",
32026 "X-Requested-With": "XMLHttpRequest"
32029 for (var headerName in headers) {
32030 var headerValue = headers[headerName];
32032 this.xhr.setRequestHeader(headerName, headerValue);
32038 this.xhr.onload = function()
32040 _this.xhrOnLoad(_this.xhr);
32043 this.xhr.onerror = function()
32045 _this.xhrOnError(_this.xhr);
32048 var formData = new FormData();
32050 formData.append('returnHTML', 'NO');
32053 formData.append('crop', crop);
32056 formData.append(this.paramName, file, file.name);
32063 if(this.fireEvent('prepare', this, formData, options) != false){
32065 if(options.manually){
32069 this.xhr.send(formData);
32073 this.uploadCancel();
32076 uploadCancel : function()
32082 this.delegates = [];
32084 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32091 renderPreview : function(file)
32093 if(typeof(file.target) != 'undefined' && file.target){
32097 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32099 var previewEl = this.managerEl.createChild({
32101 cls : 'roo-document-manager-preview',
32105 tooltip : file[this.toolTipName],
32106 cls : 'roo-document-manager-thumb',
32107 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32112 html : '<i class="fa fa-times-circle"></i>'
32117 var close = previewEl.select('button.close', true).first();
32119 close.on('click', this.onRemove, this, file);
32121 file.target = previewEl;
32123 var image = previewEl.select('img', true).first();
32127 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32129 image.on('click', this.onClick, this, file);
32131 this.fireEvent('previewrendered', this, file);
32137 onPreviewLoad : function(file, image)
32139 if(typeof(file.target) == 'undefined' || !file.target){
32143 var width = image.dom.naturalWidth || image.dom.width;
32144 var height = image.dom.naturalHeight || image.dom.height;
32146 if(!this.previewResize) {
32150 if(width > height){
32151 file.target.addClass('wide');
32155 file.target.addClass('tall');
32160 uploadFromSource : function(file, crop)
32162 this.xhr = new XMLHttpRequest();
32164 this.managerEl.createChild({
32166 cls : 'roo-document-manager-loading',
32170 tooltip : file.name,
32171 cls : 'roo-document-manager-thumb',
32172 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32178 this.xhr.open(this.method, this.url, true);
32181 "Accept": "application/json",
32182 "Cache-Control": "no-cache",
32183 "X-Requested-With": "XMLHttpRequest"
32186 for (var headerName in headers) {
32187 var headerValue = headers[headerName];
32189 this.xhr.setRequestHeader(headerName, headerValue);
32195 this.xhr.onload = function()
32197 _this.xhrOnLoad(_this.xhr);
32200 this.xhr.onerror = function()
32202 _this.xhrOnError(_this.xhr);
32205 var formData = new FormData();
32207 formData.append('returnHTML', 'NO');
32209 formData.append('crop', crop);
32211 if(typeof(file.filename) != 'undefined'){
32212 formData.append('filename', file.filename);
32215 if(typeof(file.mimetype) != 'undefined'){
32216 formData.append('mimetype', file.mimetype);
32221 if(this.fireEvent('prepare', this, formData) != false){
32222 this.xhr.send(formData);
32232 * @class Roo.bootstrap.DocumentViewer
32233 * @extends Roo.bootstrap.Component
32234 * Bootstrap DocumentViewer class
32235 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32236 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32239 * Create a new DocumentViewer
32240 * @param {Object} config The config object
32243 Roo.bootstrap.DocumentViewer = function(config){
32244 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32249 * Fire after initEvent
32250 * @param {Roo.bootstrap.DocumentViewer} this
32256 * @param {Roo.bootstrap.DocumentViewer} this
32261 * Fire after download button
32262 * @param {Roo.bootstrap.DocumentViewer} this
32267 * Fire after trash button
32268 * @param {Roo.bootstrap.DocumentViewer} this
32275 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32277 showDownload : true,
32281 getAutoCreate : function()
32285 cls : 'roo-document-viewer',
32289 cls : 'roo-document-viewer-body',
32293 cls : 'roo-document-viewer-thumb',
32297 cls : 'roo-document-viewer-image'
32305 cls : 'roo-document-viewer-footer',
32308 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32312 cls : 'btn-group roo-document-viewer-download',
32316 cls : 'btn btn-default',
32317 html : '<i class="fa fa-download"></i>'
32323 cls : 'btn-group roo-document-viewer-trash',
32327 cls : 'btn btn-default',
32328 html : '<i class="fa fa-trash"></i>'
32341 initEvents : function()
32343 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32344 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32346 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32347 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32349 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32350 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32352 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32353 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32355 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32356 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32358 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32359 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32361 this.bodyEl.on('click', this.onClick, this);
32362 this.downloadBtn.on('click', this.onDownload, this);
32363 this.trashBtn.on('click', this.onTrash, this);
32365 this.downloadBtn.hide();
32366 this.trashBtn.hide();
32368 if(this.showDownload){
32369 this.downloadBtn.show();
32372 if(this.showTrash){
32373 this.trashBtn.show();
32376 if(!this.showDownload && !this.showTrash) {
32377 this.footerEl.hide();
32382 initial : function()
32384 this.fireEvent('initial', this);
32388 onClick : function(e)
32390 e.preventDefault();
32392 this.fireEvent('click', this);
32395 onDownload : function(e)
32397 e.preventDefault();
32399 this.fireEvent('download', this);
32402 onTrash : function(e)
32404 e.preventDefault();
32406 this.fireEvent('trash', this);
32418 * @class Roo.bootstrap.NavProgressBar
32419 * @extends Roo.bootstrap.Component
32420 * Bootstrap NavProgressBar class
32423 * Create a new nav progress bar
32424 * @param {Object} config The config object
32427 Roo.bootstrap.NavProgressBar = function(config){
32428 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32430 this.bullets = this.bullets || [];
32432 // Roo.bootstrap.NavProgressBar.register(this);
32436 * Fires when the active item changes
32437 * @param {Roo.bootstrap.NavProgressBar} this
32438 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32439 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32446 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32451 getAutoCreate : function()
32453 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32457 cls : 'roo-navigation-bar-group',
32461 cls : 'roo-navigation-top-bar'
32465 cls : 'roo-navigation-bullets-bar',
32469 cls : 'roo-navigation-bar'
32476 cls : 'roo-navigation-bottom-bar'
32486 initEvents: function()
32491 onRender : function(ct, position)
32493 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32495 if(this.bullets.length){
32496 Roo.each(this.bullets, function(b){
32505 addItem : function(cfg)
32507 var item = new Roo.bootstrap.NavProgressItem(cfg);
32509 item.parentId = this.id;
32510 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32513 var top = new Roo.bootstrap.Element({
32515 cls : 'roo-navigation-bar-text'
32518 var bottom = new Roo.bootstrap.Element({
32520 cls : 'roo-navigation-bar-text'
32523 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32524 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32526 var topText = new Roo.bootstrap.Element({
32528 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32531 var bottomText = new Roo.bootstrap.Element({
32533 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32536 topText.onRender(top.el, null);
32537 bottomText.onRender(bottom.el, null);
32540 item.bottomEl = bottom;
32543 this.barItems.push(item);
32548 getActive : function()
32550 var active = false;
32552 Roo.each(this.barItems, function(v){
32554 if (!v.isActive()) {
32566 setActiveItem : function(item)
32570 Roo.each(this.barItems, function(v){
32571 if (v.rid == item.rid) {
32575 if (v.isActive()) {
32576 v.setActive(false);
32581 item.setActive(true);
32583 this.fireEvent('changed', this, item, prev);
32586 getBarItem: function(rid)
32590 Roo.each(this.barItems, function(e) {
32591 if (e.rid != rid) {
32602 indexOfItem : function(item)
32606 Roo.each(this.barItems, function(v, i){
32608 if (v.rid != item.rid) {
32619 setActiveNext : function()
32621 var i = this.indexOfItem(this.getActive());
32623 if (i > this.barItems.length) {
32627 this.setActiveItem(this.barItems[i+1]);
32630 setActivePrev : function()
32632 var i = this.indexOfItem(this.getActive());
32638 this.setActiveItem(this.barItems[i-1]);
32641 format : function()
32643 if(!this.barItems.length){
32647 var width = 100 / this.barItems.length;
32649 Roo.each(this.barItems, function(i){
32650 i.el.setStyle('width', width + '%');
32651 i.topEl.el.setStyle('width', width + '%');
32652 i.bottomEl.el.setStyle('width', width + '%');
32661 * Nav Progress Item
32666 * @class Roo.bootstrap.NavProgressItem
32667 * @extends Roo.bootstrap.Component
32668 * Bootstrap NavProgressItem class
32669 * @cfg {String} rid the reference id
32670 * @cfg {Boolean} active (true|false) Is item active default false
32671 * @cfg {Boolean} disabled (true|false) Is item active default false
32672 * @cfg {String} html
32673 * @cfg {String} position (top|bottom) text position default bottom
32674 * @cfg {String} icon show icon instead of number
32677 * Create a new NavProgressItem
32678 * @param {Object} config The config object
32680 Roo.bootstrap.NavProgressItem = function(config){
32681 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32686 * The raw click event for the entire grid.
32687 * @param {Roo.bootstrap.NavProgressItem} this
32688 * @param {Roo.EventObject} e
32695 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32701 position : 'bottom',
32704 getAutoCreate : function()
32706 var iconCls = 'roo-navigation-bar-item-icon';
32708 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32712 cls: 'roo-navigation-bar-item',
32722 cfg.cls += ' active';
32725 cfg.cls += ' disabled';
32731 disable : function()
32733 this.setDisabled(true);
32736 enable : function()
32738 this.setDisabled(false);
32741 initEvents: function()
32743 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32745 this.iconEl.on('click', this.onClick, this);
32748 onClick : function(e)
32750 e.preventDefault();
32756 if(this.fireEvent('click', this, e) === false){
32760 this.parent().setActiveItem(this);
32763 isActive: function ()
32765 return this.active;
32768 setActive : function(state)
32770 if(this.active == state){
32774 this.active = state;
32777 this.el.addClass('active');
32781 this.el.removeClass('active');
32786 setDisabled : function(state)
32788 if(this.disabled == state){
32792 this.disabled = state;
32795 this.el.addClass('disabled');
32799 this.el.removeClass('disabled');
32802 tooltipEl : function()
32804 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32817 * @class Roo.bootstrap.FieldLabel
32818 * @extends Roo.bootstrap.Component
32819 * Bootstrap FieldLabel class
32820 * @cfg {String} html contents of the element
32821 * @cfg {String} tag tag of the element default label
32822 * @cfg {String} cls class of the element
32823 * @cfg {String} target label target
32824 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32825 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32826 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32827 * @cfg {String} iconTooltip default "This field is required"
32828 * @cfg {String} indicatorpos (left|right) default left
32831 * Create a new FieldLabel
32832 * @param {Object} config The config object
32835 Roo.bootstrap.FieldLabel = function(config){
32836 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32841 * Fires after the field has been marked as invalid.
32842 * @param {Roo.form.FieldLabel} this
32843 * @param {String} msg The validation message
32848 * Fires after the field has been validated with no errors.
32849 * @param {Roo.form.FieldLabel} this
32855 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32862 invalidClass : 'has-warning',
32863 validClass : 'has-success',
32864 iconTooltip : 'This field is required',
32865 indicatorpos : 'left',
32867 getAutoCreate : function(){
32870 if (!this.allowBlank) {
32876 cls : 'roo-bootstrap-field-label ' + this.cls,
32881 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32882 tooltip : this.iconTooltip
32891 if(this.indicatorpos == 'right'){
32894 cls : 'roo-bootstrap-field-label ' + this.cls,
32903 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32904 tooltip : this.iconTooltip
32913 initEvents: function()
32915 Roo.bootstrap.Element.superclass.initEvents.call(this);
32917 this.indicator = this.indicatorEl();
32919 if(this.indicator){
32920 this.indicator.removeClass('visible');
32921 this.indicator.addClass('invisible');
32924 Roo.bootstrap.FieldLabel.register(this);
32927 indicatorEl : function()
32929 var indicator = this.el.select('i.roo-required-indicator',true).first();
32940 * Mark this field as valid
32942 markValid : function()
32944 if(this.indicator){
32945 this.indicator.removeClass('visible');
32946 this.indicator.addClass('invisible');
32948 if (Roo.bootstrap.version == 3) {
32949 this.el.removeClass(this.invalidClass);
32950 this.el.addClass(this.validClass);
32952 this.el.removeClass('is-invalid');
32953 this.el.addClass('is-valid');
32957 this.fireEvent('valid', this);
32961 * Mark this field as invalid
32962 * @param {String} msg The validation message
32964 markInvalid : function(msg)
32966 if(this.indicator){
32967 this.indicator.removeClass('invisible');
32968 this.indicator.addClass('visible');
32970 if (Roo.bootstrap.version == 3) {
32971 this.el.removeClass(this.validClass);
32972 this.el.addClass(this.invalidClass);
32974 this.el.removeClass('is-valid');
32975 this.el.addClass('is-invalid');
32979 this.fireEvent('invalid', this, msg);
32985 Roo.apply(Roo.bootstrap.FieldLabel, {
32990 * register a FieldLabel Group
32991 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32993 register : function(label)
32995 if(this.groups.hasOwnProperty(label.target)){
32999 this.groups[label.target] = label;
33003 * fetch a FieldLabel Group based on the target
33004 * @param {string} target
33005 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33007 get: function(target) {
33008 if (typeof(this.groups[target]) == 'undefined') {
33012 return this.groups[target] ;
33021 * page DateSplitField.
33027 * @class Roo.bootstrap.DateSplitField
33028 * @extends Roo.bootstrap.Component
33029 * Bootstrap DateSplitField class
33030 * @cfg {string} fieldLabel - the label associated
33031 * @cfg {Number} labelWidth set the width of label (0-12)
33032 * @cfg {String} labelAlign (top|left)
33033 * @cfg {Boolean} dayAllowBlank (true|false) default false
33034 * @cfg {Boolean} monthAllowBlank (true|false) default false
33035 * @cfg {Boolean} yearAllowBlank (true|false) default false
33036 * @cfg {string} dayPlaceholder
33037 * @cfg {string} monthPlaceholder
33038 * @cfg {string} yearPlaceholder
33039 * @cfg {string} dayFormat default 'd'
33040 * @cfg {string} monthFormat default 'm'
33041 * @cfg {string} yearFormat default 'Y'
33042 * @cfg {Number} labellg set the width of label (1-12)
33043 * @cfg {Number} labelmd set the width of label (1-12)
33044 * @cfg {Number} labelsm set the width of label (1-12)
33045 * @cfg {Number} labelxs set the width of label (1-12)
33049 * Create a new DateSplitField
33050 * @param {Object} config The config object
33053 Roo.bootstrap.DateSplitField = function(config){
33054 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33060 * getting the data of years
33061 * @param {Roo.bootstrap.DateSplitField} this
33062 * @param {Object} years
33067 * getting the data of days
33068 * @param {Roo.bootstrap.DateSplitField} this
33069 * @param {Object} days
33074 * Fires after the field has been marked as invalid.
33075 * @param {Roo.form.Field} this
33076 * @param {String} msg The validation message
33081 * Fires after the field has been validated with no errors.
33082 * @param {Roo.form.Field} this
33088 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33091 labelAlign : 'top',
33093 dayAllowBlank : false,
33094 monthAllowBlank : false,
33095 yearAllowBlank : false,
33096 dayPlaceholder : '',
33097 monthPlaceholder : '',
33098 yearPlaceholder : '',
33102 isFormField : true,
33108 getAutoCreate : function()
33112 cls : 'row roo-date-split-field-group',
33117 cls : 'form-hidden-field roo-date-split-field-group-value',
33123 var labelCls = 'col-md-12';
33124 var contentCls = 'col-md-4';
33126 if(this.fieldLabel){
33130 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33134 html : this.fieldLabel
33139 if(this.labelAlign == 'left'){
33141 if(this.labelWidth > 12){
33142 label.style = "width: " + this.labelWidth + 'px';
33145 if(this.labelWidth < 13 && this.labelmd == 0){
33146 this.labelmd = this.labelWidth;
33149 if(this.labellg > 0){
33150 labelCls = ' col-lg-' + this.labellg;
33151 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33154 if(this.labelmd > 0){
33155 labelCls = ' col-md-' + this.labelmd;
33156 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33159 if(this.labelsm > 0){
33160 labelCls = ' col-sm-' + this.labelsm;
33161 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33164 if(this.labelxs > 0){
33165 labelCls = ' col-xs-' + this.labelxs;
33166 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33170 label.cls += ' ' + labelCls;
33172 cfg.cn.push(label);
33175 Roo.each(['day', 'month', 'year'], function(t){
33178 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33185 inputEl: function ()
33187 return this.el.select('.roo-date-split-field-group-value', true).first();
33190 onRender : function(ct, position)
33194 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33196 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33198 this.dayField = new Roo.bootstrap.ComboBox({
33199 allowBlank : this.dayAllowBlank,
33200 alwaysQuery : true,
33201 displayField : 'value',
33204 forceSelection : true,
33206 placeholder : this.dayPlaceholder,
33207 selectOnFocus : true,
33208 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33209 triggerAction : 'all',
33211 valueField : 'value',
33212 store : new Roo.data.SimpleStore({
33213 data : (function() {
33215 _this.fireEvent('days', _this, days);
33218 fields : [ 'value' ]
33221 select : function (_self, record, index)
33223 _this.setValue(_this.getValue());
33228 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33230 this.monthField = new Roo.bootstrap.MonthField({
33231 after : '<i class=\"fa fa-calendar\"></i>',
33232 allowBlank : this.monthAllowBlank,
33233 placeholder : this.monthPlaceholder,
33236 render : function (_self)
33238 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33239 e.preventDefault();
33243 select : function (_self, oldvalue, newvalue)
33245 _this.setValue(_this.getValue());
33250 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33252 this.yearField = new Roo.bootstrap.ComboBox({
33253 allowBlank : this.yearAllowBlank,
33254 alwaysQuery : true,
33255 displayField : 'value',
33258 forceSelection : true,
33260 placeholder : this.yearPlaceholder,
33261 selectOnFocus : true,
33262 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33263 triggerAction : 'all',
33265 valueField : 'value',
33266 store : new Roo.data.SimpleStore({
33267 data : (function() {
33269 _this.fireEvent('years', _this, years);
33272 fields : [ 'value' ]
33275 select : function (_self, record, index)
33277 _this.setValue(_this.getValue());
33282 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33285 setValue : function(v, format)
33287 this.inputEl.dom.value = v;
33289 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33291 var d = Date.parseDate(v, f);
33298 this.setDay(d.format(this.dayFormat));
33299 this.setMonth(d.format(this.monthFormat));
33300 this.setYear(d.format(this.yearFormat));
33307 setDay : function(v)
33309 this.dayField.setValue(v);
33310 this.inputEl.dom.value = this.getValue();
33315 setMonth : function(v)
33317 this.monthField.setValue(v, true);
33318 this.inputEl.dom.value = this.getValue();
33323 setYear : function(v)
33325 this.yearField.setValue(v);
33326 this.inputEl.dom.value = this.getValue();
33331 getDay : function()
33333 return this.dayField.getValue();
33336 getMonth : function()
33338 return this.monthField.getValue();
33341 getYear : function()
33343 return this.yearField.getValue();
33346 getValue : function()
33348 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33350 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33360 this.inputEl.dom.value = '';
33365 validate : function()
33367 var d = this.dayField.validate();
33368 var m = this.monthField.validate();
33369 var y = this.yearField.validate();
33374 (!this.dayAllowBlank && !d) ||
33375 (!this.monthAllowBlank && !m) ||
33376 (!this.yearAllowBlank && !y)
33381 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33390 this.markInvalid();
33395 markValid : function()
33398 var label = this.el.select('label', true).first();
33399 var icon = this.el.select('i.fa-star', true).first();
33405 this.fireEvent('valid', this);
33409 * Mark this field as invalid
33410 * @param {String} msg The validation message
33412 markInvalid : function(msg)
33415 var label = this.el.select('label', true).first();
33416 var icon = this.el.select('i.fa-star', true).first();
33418 if(label && !icon){
33419 this.el.select('.roo-date-split-field-label', true).createChild({
33421 cls : 'text-danger fa fa-lg fa-star',
33422 tooltip : 'This field is required',
33423 style : 'margin-right:5px;'
33427 this.fireEvent('invalid', this, msg);
33430 clearInvalid : function()
33432 var label = this.el.select('label', true).first();
33433 var icon = this.el.select('i.fa-star', true).first();
33439 this.fireEvent('valid', this);
33442 getName: function()
33452 * http://masonry.desandro.com
33454 * The idea is to render all the bricks based on vertical width...
33456 * The original code extends 'outlayer' - we might need to use that....
33462 * @class Roo.bootstrap.LayoutMasonry
33463 * @extends Roo.bootstrap.Component
33464 * Bootstrap Layout Masonry class
33467 * Create a new Element
33468 * @param {Object} config The config object
33471 Roo.bootstrap.LayoutMasonry = function(config){
33473 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33477 Roo.bootstrap.LayoutMasonry.register(this);
33483 * Fire after layout the items
33484 * @param {Roo.bootstrap.LayoutMasonry} this
33485 * @param {Roo.EventObject} e
33492 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33495 * @cfg {Boolean} isLayoutInstant = no animation?
33497 isLayoutInstant : false, // needed?
33500 * @cfg {Number} boxWidth width of the columns
33505 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33510 * @cfg {Number} padWidth padding below box..
33515 * @cfg {Number} gutter gutter width..
33520 * @cfg {Number} maxCols maximum number of columns
33526 * @cfg {Boolean} isAutoInitial defalut true
33528 isAutoInitial : true,
33533 * @cfg {Boolean} isHorizontal defalut false
33535 isHorizontal : false,
33537 currentSize : null,
33543 bricks: null, //CompositeElement
33547 _isLayoutInited : false,
33549 // isAlternative : false, // only use for vertical layout...
33552 * @cfg {Number} alternativePadWidth padding below box..
33554 alternativePadWidth : 50,
33556 selectedBrick : [],
33558 getAutoCreate : function(){
33560 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33564 cls: 'blog-masonary-wrapper ' + this.cls,
33566 cls : 'mas-boxes masonary'
33573 getChildContainer: function( )
33575 if (this.boxesEl) {
33576 return this.boxesEl;
33579 this.boxesEl = this.el.select('.mas-boxes').first();
33581 return this.boxesEl;
33585 initEvents : function()
33589 if(this.isAutoInitial){
33590 Roo.log('hook children rendered');
33591 this.on('childrenrendered', function() {
33592 Roo.log('children rendered');
33598 initial : function()
33600 this.selectedBrick = [];
33602 this.currentSize = this.el.getBox(true);
33604 Roo.EventManager.onWindowResize(this.resize, this);
33606 if(!this.isAutoInitial){
33614 //this.layout.defer(500,this);
33618 resize : function()
33620 var cs = this.el.getBox(true);
33623 this.currentSize.width == cs.width &&
33624 this.currentSize.x == cs.x &&
33625 this.currentSize.height == cs.height &&
33626 this.currentSize.y == cs.y
33628 Roo.log("no change in with or X or Y");
33632 this.currentSize = cs;
33638 layout : function()
33640 this._resetLayout();
33642 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33644 this.layoutItems( isInstant );
33646 this._isLayoutInited = true;
33648 this.fireEvent('layout', this);
33652 _resetLayout : function()
33654 if(this.isHorizontal){
33655 this.horizontalMeasureColumns();
33659 this.verticalMeasureColumns();
33663 verticalMeasureColumns : function()
33665 this.getContainerWidth();
33667 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33668 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33672 var boxWidth = this.boxWidth + this.padWidth;
33674 if(this.containerWidth < this.boxWidth){
33675 boxWidth = this.containerWidth
33678 var containerWidth = this.containerWidth;
33680 var cols = Math.floor(containerWidth / boxWidth);
33682 this.cols = Math.max( cols, 1 );
33684 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33686 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33688 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33690 this.colWidth = boxWidth + avail - this.padWidth;
33692 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33693 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33696 horizontalMeasureColumns : function()
33698 this.getContainerWidth();
33700 var boxWidth = this.boxWidth;
33702 if(this.containerWidth < boxWidth){
33703 boxWidth = this.containerWidth;
33706 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33708 this.el.setHeight(boxWidth);
33712 getContainerWidth : function()
33714 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33717 layoutItems : function( isInstant )
33719 Roo.log(this.bricks);
33721 var items = Roo.apply([], this.bricks);
33723 if(this.isHorizontal){
33724 this._horizontalLayoutItems( items , isInstant );
33728 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33729 // this._verticalAlternativeLayoutItems( items , isInstant );
33733 this._verticalLayoutItems( items , isInstant );
33737 _verticalLayoutItems : function ( items , isInstant)
33739 if ( !items || !items.length ) {
33744 ['xs', 'xs', 'xs', 'tall'],
33745 ['xs', 'xs', 'tall'],
33746 ['xs', 'xs', 'sm'],
33747 ['xs', 'xs', 'xs'],
33753 ['sm', 'xs', 'xs'],
33757 ['tall', 'xs', 'xs', 'xs'],
33758 ['tall', 'xs', 'xs'],
33770 Roo.each(items, function(item, k){
33772 switch (item.size) {
33773 // these layouts take up a full box,
33784 boxes.push([item]);
33807 var filterPattern = function(box, length)
33815 var pattern = box.slice(0, length);
33819 Roo.each(pattern, function(i){
33820 format.push(i.size);
33823 Roo.each(standard, function(s){
33825 if(String(s) != String(format)){
33834 if(!match && length == 1){
33839 filterPattern(box, length - 1);
33843 queue.push(pattern);
33845 box = box.slice(length, box.length);
33847 filterPattern(box, 4);
33853 Roo.each(boxes, function(box, k){
33859 if(box.length == 1){
33864 filterPattern(box, 4);
33868 this._processVerticalLayoutQueue( queue, isInstant );
33872 // _verticalAlternativeLayoutItems : function( items , isInstant )
33874 // if ( !items || !items.length ) {
33878 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33882 _horizontalLayoutItems : function ( items , isInstant)
33884 if ( !items || !items.length || items.length < 3) {
33890 var eItems = items.slice(0, 3);
33892 items = items.slice(3, items.length);
33895 ['xs', 'xs', 'xs', 'wide'],
33896 ['xs', 'xs', 'wide'],
33897 ['xs', 'xs', 'sm'],
33898 ['xs', 'xs', 'xs'],
33904 ['sm', 'xs', 'xs'],
33908 ['wide', 'xs', 'xs', 'xs'],
33909 ['wide', 'xs', 'xs'],
33922 Roo.each(items, function(item, k){
33924 switch (item.size) {
33935 boxes.push([item]);
33959 var filterPattern = function(box, length)
33967 var pattern = box.slice(0, length);
33971 Roo.each(pattern, function(i){
33972 format.push(i.size);
33975 Roo.each(standard, function(s){
33977 if(String(s) != String(format)){
33986 if(!match && length == 1){
33991 filterPattern(box, length - 1);
33995 queue.push(pattern);
33997 box = box.slice(length, box.length);
33999 filterPattern(box, 4);
34005 Roo.each(boxes, function(box, k){
34011 if(box.length == 1){
34016 filterPattern(box, 4);
34023 var pos = this.el.getBox(true);
34027 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34029 var hit_end = false;
34031 Roo.each(queue, function(box){
34035 Roo.each(box, function(b){
34037 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34047 Roo.each(box, function(b){
34049 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34052 mx = Math.max(mx, b.x);
34056 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34060 Roo.each(box, function(b){
34062 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34076 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34079 /** Sets position of item in DOM
34080 * @param {Element} item
34081 * @param {Number} x - horizontal position
34082 * @param {Number} y - vertical position
34083 * @param {Boolean} isInstant - disables transitions
34085 _processVerticalLayoutQueue : function( queue, isInstant )
34087 var pos = this.el.getBox(true);
34092 for (var i = 0; i < this.cols; i++){
34096 Roo.each(queue, function(box, k){
34098 var col = k % this.cols;
34100 Roo.each(box, function(b,kk){
34102 b.el.position('absolute');
34104 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34105 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34107 if(b.size == 'md-left' || b.size == 'md-right'){
34108 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34109 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34112 b.el.setWidth(width);
34113 b.el.setHeight(height);
34115 b.el.select('iframe',true).setSize(width,height);
34119 for (var i = 0; i < this.cols; i++){
34121 if(maxY[i] < maxY[col]){
34126 col = Math.min(col, i);
34130 x = pos.x + col * (this.colWidth + this.padWidth);
34134 var positions = [];
34136 switch (box.length){
34138 positions = this.getVerticalOneBoxColPositions(x, y, box);
34141 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34144 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34147 positions = this.getVerticalFourBoxColPositions(x, y, box);
34153 Roo.each(box, function(b,kk){
34155 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34157 var sz = b.el.getSize();
34159 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34167 for (var i = 0; i < this.cols; i++){
34168 mY = Math.max(mY, maxY[i]);
34171 this.el.setHeight(mY - pos.y);
34175 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34177 // var pos = this.el.getBox(true);
34180 // var maxX = pos.right;
34182 // var maxHeight = 0;
34184 // Roo.each(items, function(item, k){
34188 // item.el.position('absolute');
34190 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34192 // item.el.setWidth(width);
34194 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34196 // item.el.setHeight(height);
34199 // item.el.setXY([x, y], isInstant ? false : true);
34201 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34204 // y = y + height + this.alternativePadWidth;
34206 // maxHeight = maxHeight + height + this.alternativePadWidth;
34210 // this.el.setHeight(maxHeight);
34214 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34216 var pos = this.el.getBox(true);
34221 var maxX = pos.right;
34223 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34225 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34227 Roo.each(queue, function(box, k){
34229 Roo.each(box, function(b, kk){
34231 b.el.position('absolute');
34233 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34234 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34236 if(b.size == 'md-left' || b.size == 'md-right'){
34237 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34238 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34241 b.el.setWidth(width);
34242 b.el.setHeight(height);
34250 var positions = [];
34252 switch (box.length){
34254 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34257 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34260 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34263 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34269 Roo.each(box, function(b,kk){
34271 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34273 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34281 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34283 Roo.each(eItems, function(b,k){
34285 b.size = (k == 0) ? 'sm' : 'xs';
34286 b.x = (k == 0) ? 2 : 1;
34287 b.y = (k == 0) ? 2 : 1;
34289 b.el.position('absolute');
34291 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34293 b.el.setWidth(width);
34295 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34297 b.el.setHeight(height);
34301 var positions = [];
34304 x : maxX - this.unitWidth * 2 - this.gutter,
34309 x : maxX - this.unitWidth,
34310 y : minY + (this.unitWidth + this.gutter) * 2
34314 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34318 Roo.each(eItems, function(b,k){
34320 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34326 getVerticalOneBoxColPositions : function(x, y, box)
34330 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34332 if(box[0].size == 'md-left'){
34336 if(box[0].size == 'md-right'){
34341 x : x + (this.unitWidth + this.gutter) * rand,
34348 getVerticalTwoBoxColPositions : function(x, y, box)
34352 if(box[0].size == 'xs'){
34356 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34360 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34374 x : x + (this.unitWidth + this.gutter) * 2,
34375 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34382 getVerticalThreeBoxColPositions : function(x, y, box)
34386 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34394 x : x + (this.unitWidth + this.gutter) * 1,
34399 x : x + (this.unitWidth + this.gutter) * 2,
34407 if(box[0].size == 'xs' && box[1].size == 'xs'){
34416 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34420 x : x + (this.unitWidth + this.gutter) * 1,
34434 x : x + (this.unitWidth + this.gutter) * 2,
34439 x : x + (this.unitWidth + this.gutter) * 2,
34440 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34447 getVerticalFourBoxColPositions : function(x, y, box)
34451 if(box[0].size == 'xs'){
34460 y : y + (this.unitHeight + this.gutter) * 1
34465 y : y + (this.unitHeight + this.gutter) * 2
34469 x : x + (this.unitWidth + this.gutter) * 1,
34483 x : x + (this.unitWidth + this.gutter) * 2,
34488 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34489 y : y + (this.unitHeight + this.gutter) * 1
34493 x : x + (this.unitWidth + this.gutter) * 2,
34494 y : y + (this.unitWidth + this.gutter) * 2
34501 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34505 if(box[0].size == 'md-left'){
34507 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34514 if(box[0].size == 'md-right'){
34516 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34517 y : minY + (this.unitWidth + this.gutter) * 1
34523 var rand = Math.floor(Math.random() * (4 - box[0].y));
34526 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34527 y : minY + (this.unitWidth + this.gutter) * rand
34534 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34538 if(box[0].size == 'xs'){
34541 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34546 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34547 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34555 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34560 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34561 y : minY + (this.unitWidth + this.gutter) * 2
34568 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34572 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34575 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34580 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34581 y : minY + (this.unitWidth + this.gutter) * 1
34585 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34586 y : minY + (this.unitWidth + this.gutter) * 2
34593 if(box[0].size == 'xs' && box[1].size == 'xs'){
34596 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34601 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34606 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34607 y : minY + (this.unitWidth + this.gutter) * 1
34615 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34620 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34621 y : minY + (this.unitWidth + this.gutter) * 2
34625 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34626 y : minY + (this.unitWidth + this.gutter) * 2
34633 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34637 if(box[0].size == 'xs'){
34640 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34645 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34650 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),
34655 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34656 y : minY + (this.unitWidth + this.gutter) * 1
34664 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34669 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34670 y : minY + (this.unitWidth + this.gutter) * 2
34674 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34675 y : minY + (this.unitWidth + this.gutter) * 2
34679 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),
34680 y : minY + (this.unitWidth + this.gutter) * 2
34688 * remove a Masonry Brick
34689 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34691 removeBrick : function(brick_id)
34697 for (var i = 0; i<this.bricks.length; i++) {
34698 if (this.bricks[i].id == brick_id) {
34699 this.bricks.splice(i,1);
34700 this.el.dom.removeChild(Roo.get(brick_id).dom);
34707 * adds a Masonry Brick
34708 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34710 addBrick : function(cfg)
34712 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34713 //this.register(cn);
34714 cn.parentId = this.id;
34715 cn.render(this.el);
34720 * register a Masonry Brick
34721 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34724 register : function(brick)
34726 this.bricks.push(brick);
34727 brick.masonryId = this.id;
34731 * clear all the Masonry Brick
34733 clearAll : function()
34736 //this.getChildContainer().dom.innerHTML = "";
34737 this.el.dom.innerHTML = '';
34740 getSelected : function()
34742 if (!this.selectedBrick) {
34746 return this.selectedBrick;
34750 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34754 * register a Masonry Layout
34755 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34758 register : function(layout)
34760 this.groups[layout.id] = layout;
34763 * fetch a Masonry Layout based on the masonry layout ID
34764 * @param {string} the masonry layout to add
34765 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34768 get: function(layout_id) {
34769 if (typeof(this.groups[layout_id]) == 'undefined') {
34772 return this.groups[layout_id] ;
34784 * http://masonry.desandro.com
34786 * The idea is to render all the bricks based on vertical width...
34788 * The original code extends 'outlayer' - we might need to use that....
34794 * @class Roo.bootstrap.LayoutMasonryAuto
34795 * @extends Roo.bootstrap.Component
34796 * Bootstrap Layout Masonry class
34799 * Create a new Element
34800 * @param {Object} config The config object
34803 Roo.bootstrap.LayoutMasonryAuto = function(config){
34804 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34807 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34810 * @cfg {Boolean} isFitWidth - resize the width..
34812 isFitWidth : false, // options..
34814 * @cfg {Boolean} isOriginLeft = left align?
34816 isOriginLeft : true,
34818 * @cfg {Boolean} isOriginTop = top align?
34820 isOriginTop : false,
34822 * @cfg {Boolean} isLayoutInstant = no animation?
34824 isLayoutInstant : false, // needed?
34826 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34828 isResizingContainer : true,
34830 * @cfg {Number} columnWidth width of the columns
34836 * @cfg {Number} maxCols maximum number of columns
34841 * @cfg {Number} padHeight padding below box..
34847 * @cfg {Boolean} isAutoInitial defalut true
34850 isAutoInitial : true,
34856 initialColumnWidth : 0,
34857 currentSize : null,
34859 colYs : null, // array.
34866 bricks: null, //CompositeElement
34867 cols : 0, // array?
34868 // element : null, // wrapped now this.el
34869 _isLayoutInited : null,
34872 getAutoCreate : function(){
34876 cls: 'blog-masonary-wrapper ' + this.cls,
34878 cls : 'mas-boxes masonary'
34885 getChildContainer: function( )
34887 if (this.boxesEl) {
34888 return this.boxesEl;
34891 this.boxesEl = this.el.select('.mas-boxes').first();
34893 return this.boxesEl;
34897 initEvents : function()
34901 if(this.isAutoInitial){
34902 Roo.log('hook children rendered');
34903 this.on('childrenrendered', function() {
34904 Roo.log('children rendered');
34911 initial : function()
34913 this.reloadItems();
34915 this.currentSize = this.el.getBox(true);
34917 /// was window resize... - let's see if this works..
34918 Roo.EventManager.onWindowResize(this.resize, this);
34920 if(!this.isAutoInitial){
34925 this.layout.defer(500,this);
34928 reloadItems: function()
34930 this.bricks = this.el.select('.masonry-brick', true);
34932 this.bricks.each(function(b) {
34933 //Roo.log(b.getSize());
34934 if (!b.attr('originalwidth')) {
34935 b.attr('originalwidth', b.getSize().width);
34940 Roo.log(this.bricks.elements.length);
34943 resize : function()
34946 var cs = this.el.getBox(true);
34948 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34949 Roo.log("no change in with or X");
34952 this.currentSize = cs;
34956 layout : function()
34959 this._resetLayout();
34960 //this._manageStamps();
34962 // don't animate first layout
34963 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34964 this.layoutItems( isInstant );
34966 // flag for initalized
34967 this._isLayoutInited = true;
34970 layoutItems : function( isInstant )
34972 //var items = this._getItemsForLayout( this.items );
34973 // original code supports filtering layout items.. we just ignore it..
34975 this._layoutItems( this.bricks , isInstant );
34977 this._postLayout();
34979 _layoutItems : function ( items , isInstant)
34981 //this.fireEvent( 'layout', this, items );
34984 if ( !items || !items.elements.length ) {
34985 // no items, emit event with empty array
34990 items.each(function(item) {
34991 Roo.log("layout item");
34993 // get x/y object from method
34994 var position = this._getItemLayoutPosition( item );
34996 position.item = item;
34997 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34998 queue.push( position );
35001 this._processLayoutQueue( queue );
35003 /** Sets position of item in DOM
35004 * @param {Element} item
35005 * @param {Number} x - horizontal position
35006 * @param {Number} y - vertical position
35007 * @param {Boolean} isInstant - disables transitions
35009 _processLayoutQueue : function( queue )
35011 for ( var i=0, len = queue.length; i < len; i++ ) {
35012 var obj = queue[i];
35013 obj.item.position('absolute');
35014 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35020 * Any logic you want to do after each layout,
35021 * i.e. size the container
35023 _postLayout : function()
35025 this.resizeContainer();
35028 resizeContainer : function()
35030 if ( !this.isResizingContainer ) {
35033 var size = this._getContainerSize();
35035 this.el.setSize(size.width,size.height);
35036 this.boxesEl.setSize(size.width,size.height);
35042 _resetLayout : function()
35044 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35045 this.colWidth = this.el.getWidth();
35046 //this.gutter = this.el.getWidth();
35048 this.measureColumns();
35054 this.colYs.push( 0 );
35060 measureColumns : function()
35062 this.getContainerWidth();
35063 // if columnWidth is 0, default to outerWidth of first item
35064 if ( !this.columnWidth ) {
35065 var firstItem = this.bricks.first();
35066 Roo.log(firstItem);
35067 this.columnWidth = this.containerWidth;
35068 if (firstItem && firstItem.attr('originalwidth') ) {
35069 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35071 // columnWidth fall back to item of first element
35072 Roo.log("set column width?");
35073 this.initialColumnWidth = this.columnWidth ;
35075 // if first elem has no width, default to size of container
35080 if (this.initialColumnWidth) {
35081 this.columnWidth = this.initialColumnWidth;
35086 // column width is fixed at the top - however if container width get's smaller we should
35089 // this bit calcs how man columns..
35091 var columnWidth = this.columnWidth += this.gutter;
35093 // calculate columns
35094 var containerWidth = this.containerWidth + this.gutter;
35096 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35097 // fix rounding errors, typically with gutters
35098 var excess = columnWidth - containerWidth % columnWidth;
35101 // if overshoot is less than a pixel, round up, otherwise floor it
35102 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35103 cols = Math[ mathMethod ]( cols );
35104 this.cols = Math.max( cols, 1 );
35105 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35107 // padding positioning..
35108 var totalColWidth = this.cols * this.columnWidth;
35109 var padavail = this.containerWidth - totalColWidth;
35110 // so for 2 columns - we need 3 'pads'
35112 var padNeeded = (1+this.cols) * this.padWidth;
35114 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35116 this.columnWidth += padExtra
35117 //this.padWidth = Math.floor(padavail / ( this.cols));
35119 // adjust colum width so that padding is fixed??
35121 // we have 3 columns ... total = width * 3
35122 // we have X left over... that should be used by
35124 //if (this.expandC) {
35132 getContainerWidth : function()
35134 /* // container is parent if fit width
35135 var container = this.isFitWidth ? this.element.parentNode : this.element;
35136 // check that this.size and size are there
35137 // IE8 triggers resize on body size change, so they might not be
35139 var size = getSize( container ); //FIXME
35140 this.containerWidth = size && size.innerWidth; //FIXME
35143 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35147 _getItemLayoutPosition : function( item ) // what is item?
35149 // we resize the item to our columnWidth..
35151 item.setWidth(this.columnWidth);
35152 item.autoBoxAdjust = false;
35154 var sz = item.getSize();
35156 // how many columns does this brick span
35157 var remainder = this.containerWidth % this.columnWidth;
35159 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35160 // round if off by 1 pixel, otherwise use ceil
35161 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35162 colSpan = Math.min( colSpan, this.cols );
35164 // normally this should be '1' as we dont' currently allow multi width columns..
35166 var colGroup = this._getColGroup( colSpan );
35167 // get the minimum Y value from the columns
35168 var minimumY = Math.min.apply( Math, colGroup );
35169 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35171 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35173 // position the brick
35175 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35176 y: this.currentSize.y + minimumY + this.padHeight
35180 // apply setHeight to necessary columns
35181 var setHeight = minimumY + sz.height + this.padHeight;
35182 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35184 var setSpan = this.cols + 1 - colGroup.length;
35185 for ( var i = 0; i < setSpan; i++ ) {
35186 this.colYs[ shortColIndex + i ] = setHeight ;
35193 * @param {Number} colSpan - number of columns the element spans
35194 * @returns {Array} colGroup
35196 _getColGroup : function( colSpan )
35198 if ( colSpan < 2 ) {
35199 // if brick spans only one column, use all the column Ys
35204 // how many different places could this brick fit horizontally
35205 var groupCount = this.cols + 1 - colSpan;
35206 // for each group potential horizontal position
35207 for ( var i = 0; i < groupCount; i++ ) {
35208 // make an array of colY values for that one group
35209 var groupColYs = this.colYs.slice( i, i + colSpan );
35210 // and get the max value of the array
35211 colGroup[i] = Math.max.apply( Math, groupColYs );
35216 _manageStamp : function( stamp )
35218 var stampSize = stamp.getSize();
35219 var offset = stamp.getBox();
35220 // get the columns that this stamp affects
35221 var firstX = this.isOriginLeft ? offset.x : offset.right;
35222 var lastX = firstX + stampSize.width;
35223 var firstCol = Math.floor( firstX / this.columnWidth );
35224 firstCol = Math.max( 0, firstCol );
35226 var lastCol = Math.floor( lastX / this.columnWidth );
35227 // lastCol should not go over if multiple of columnWidth #425
35228 lastCol -= lastX % this.columnWidth ? 0 : 1;
35229 lastCol = Math.min( this.cols - 1, lastCol );
35231 // set colYs to bottom of the stamp
35232 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35235 for ( var i = firstCol; i <= lastCol; i++ ) {
35236 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35241 _getContainerSize : function()
35243 this.maxY = Math.max.apply( Math, this.colYs );
35248 if ( this.isFitWidth ) {
35249 size.width = this._getContainerFitWidth();
35255 _getContainerFitWidth : function()
35257 var unusedCols = 0;
35258 // count unused columns
35261 if ( this.colYs[i] !== 0 ) {
35266 // fit container to columns that have been used
35267 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35270 needsResizeLayout : function()
35272 var previousWidth = this.containerWidth;
35273 this.getContainerWidth();
35274 return previousWidth !== this.containerWidth;
35289 * @class Roo.bootstrap.MasonryBrick
35290 * @extends Roo.bootstrap.Component
35291 * Bootstrap MasonryBrick class
35294 * Create a new MasonryBrick
35295 * @param {Object} config The config object
35298 Roo.bootstrap.MasonryBrick = function(config){
35300 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35302 Roo.bootstrap.MasonryBrick.register(this);
35308 * When a MasonryBrick is clcik
35309 * @param {Roo.bootstrap.MasonryBrick} this
35310 * @param {Roo.EventObject} e
35316 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35319 * @cfg {String} title
35323 * @cfg {String} html
35327 * @cfg {String} bgimage
35331 * @cfg {String} videourl
35335 * @cfg {String} cls
35339 * @cfg {String} href
35343 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35348 * @cfg {String} placetitle (center|bottom)
35353 * @cfg {Boolean} isFitContainer defalut true
35355 isFitContainer : true,
35358 * @cfg {Boolean} preventDefault defalut false
35360 preventDefault : false,
35363 * @cfg {Boolean} inverse defalut false
35365 maskInverse : false,
35367 getAutoCreate : function()
35369 if(!this.isFitContainer){
35370 return this.getSplitAutoCreate();
35373 var cls = 'masonry-brick masonry-brick-full';
35375 if(this.href.length){
35376 cls += ' masonry-brick-link';
35379 if(this.bgimage.length){
35380 cls += ' masonry-brick-image';
35383 if(this.maskInverse){
35384 cls += ' mask-inverse';
35387 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35388 cls += ' enable-mask';
35392 cls += ' masonry-' + this.size + '-brick';
35395 if(this.placetitle.length){
35397 switch (this.placetitle) {
35399 cls += ' masonry-center-title';
35402 cls += ' masonry-bottom-title';
35409 if(!this.html.length && !this.bgimage.length){
35410 cls += ' masonry-center-title';
35413 if(!this.html.length && this.bgimage.length){
35414 cls += ' masonry-bottom-title';
35419 cls += ' ' + this.cls;
35423 tag: (this.href.length) ? 'a' : 'div',
35428 cls: 'masonry-brick-mask'
35432 cls: 'masonry-brick-paragraph',
35438 if(this.href.length){
35439 cfg.href = this.href;
35442 var cn = cfg.cn[1].cn;
35444 if(this.title.length){
35447 cls: 'masonry-brick-title',
35452 if(this.html.length){
35455 cls: 'masonry-brick-text',
35460 if (!this.title.length && !this.html.length) {
35461 cfg.cn[1].cls += ' hide';
35464 if(this.bgimage.length){
35467 cls: 'masonry-brick-image-view',
35472 if(this.videourl.length){
35473 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35474 // youtube support only?
35477 cls: 'masonry-brick-image-view',
35480 allowfullscreen : true
35488 getSplitAutoCreate : function()
35490 var cls = 'masonry-brick masonry-brick-split';
35492 if(this.href.length){
35493 cls += ' masonry-brick-link';
35496 if(this.bgimage.length){
35497 cls += ' masonry-brick-image';
35501 cls += ' masonry-' + this.size + '-brick';
35504 switch (this.placetitle) {
35506 cls += ' masonry-center-title';
35509 cls += ' masonry-bottom-title';
35512 if(!this.bgimage.length){
35513 cls += ' masonry-center-title';
35516 if(this.bgimage.length){
35517 cls += ' masonry-bottom-title';
35523 cls += ' ' + this.cls;
35527 tag: (this.href.length) ? 'a' : 'div',
35532 cls: 'masonry-brick-split-head',
35536 cls: 'masonry-brick-paragraph',
35543 cls: 'masonry-brick-split-body',
35549 if(this.href.length){
35550 cfg.href = this.href;
35553 if(this.title.length){
35554 cfg.cn[0].cn[0].cn.push({
35556 cls: 'masonry-brick-title',
35561 if(this.html.length){
35562 cfg.cn[1].cn.push({
35564 cls: 'masonry-brick-text',
35569 if(this.bgimage.length){
35570 cfg.cn[0].cn.push({
35572 cls: 'masonry-brick-image-view',
35577 if(this.videourl.length){
35578 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35579 // youtube support only?
35580 cfg.cn[0].cn.cn.push({
35582 cls: 'masonry-brick-image-view',
35585 allowfullscreen : true
35592 initEvents: function()
35594 switch (this.size) {
35627 this.el.on('touchstart', this.onTouchStart, this);
35628 this.el.on('touchmove', this.onTouchMove, this);
35629 this.el.on('touchend', this.onTouchEnd, this);
35630 this.el.on('contextmenu', this.onContextMenu, this);
35632 this.el.on('mouseenter' ,this.enter, this);
35633 this.el.on('mouseleave', this.leave, this);
35634 this.el.on('click', this.onClick, this);
35637 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35638 this.parent().bricks.push(this);
35643 onClick: function(e, el)
35645 var time = this.endTimer - this.startTimer;
35646 // Roo.log(e.preventDefault());
35649 e.preventDefault();
35654 if(!this.preventDefault){
35658 e.preventDefault();
35660 if (this.activeClass != '') {
35661 this.selectBrick();
35664 this.fireEvent('click', this, e);
35667 enter: function(e, el)
35669 e.preventDefault();
35671 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35675 if(this.bgimage.length && this.html.length){
35676 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35680 leave: function(e, el)
35682 e.preventDefault();
35684 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35688 if(this.bgimage.length && this.html.length){
35689 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35693 onTouchStart: function(e, el)
35695 // e.preventDefault();
35697 this.touchmoved = false;
35699 if(!this.isFitContainer){
35703 if(!this.bgimage.length || !this.html.length){
35707 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35709 this.timer = new Date().getTime();
35713 onTouchMove: function(e, el)
35715 this.touchmoved = true;
35718 onContextMenu : function(e,el)
35720 e.preventDefault();
35721 e.stopPropagation();
35725 onTouchEnd: function(e, el)
35727 // e.preventDefault();
35729 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35736 if(!this.bgimage.length || !this.html.length){
35738 if(this.href.length){
35739 window.location.href = this.href;
35745 if(!this.isFitContainer){
35749 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35751 window.location.href = this.href;
35754 //selection on single brick only
35755 selectBrick : function() {
35757 if (!this.parentId) {
35761 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35762 var index = m.selectedBrick.indexOf(this.id);
35765 m.selectedBrick.splice(index,1);
35766 this.el.removeClass(this.activeClass);
35770 for(var i = 0; i < m.selectedBrick.length; i++) {
35771 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35772 b.el.removeClass(b.activeClass);
35775 m.selectedBrick = [];
35777 m.selectedBrick.push(this.id);
35778 this.el.addClass(this.activeClass);
35782 isSelected : function(){
35783 return this.el.hasClass(this.activeClass);
35788 Roo.apply(Roo.bootstrap.MasonryBrick, {
35791 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35793 * register a Masonry Brick
35794 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35797 register : function(brick)
35799 //this.groups[brick.id] = brick;
35800 this.groups.add(brick.id, brick);
35803 * fetch a masonry brick based on the masonry brick ID
35804 * @param {string} the masonry brick to add
35805 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35808 get: function(brick_id)
35810 // if (typeof(this.groups[brick_id]) == 'undefined') {
35813 // return this.groups[brick_id] ;
35815 if(this.groups.key(brick_id)) {
35816 return this.groups.key(brick_id);
35834 * @class Roo.bootstrap.Brick
35835 * @extends Roo.bootstrap.Component
35836 * Bootstrap Brick class
35839 * Create a new Brick
35840 * @param {Object} config The config object
35843 Roo.bootstrap.Brick = function(config){
35844 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35850 * When a Brick is click
35851 * @param {Roo.bootstrap.Brick} this
35852 * @param {Roo.EventObject} e
35858 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35861 * @cfg {String} title
35865 * @cfg {String} html
35869 * @cfg {String} bgimage
35873 * @cfg {String} cls
35877 * @cfg {String} href
35881 * @cfg {String} video
35885 * @cfg {Boolean} square
35889 getAutoCreate : function()
35891 var cls = 'roo-brick';
35893 if(this.href.length){
35894 cls += ' roo-brick-link';
35897 if(this.bgimage.length){
35898 cls += ' roo-brick-image';
35901 if(!this.html.length && !this.bgimage.length){
35902 cls += ' roo-brick-center-title';
35905 if(!this.html.length && this.bgimage.length){
35906 cls += ' roo-brick-bottom-title';
35910 cls += ' ' + this.cls;
35914 tag: (this.href.length) ? 'a' : 'div',
35919 cls: 'roo-brick-paragraph',
35925 if(this.href.length){
35926 cfg.href = this.href;
35929 var cn = cfg.cn[0].cn;
35931 if(this.title.length){
35934 cls: 'roo-brick-title',
35939 if(this.html.length){
35942 cls: 'roo-brick-text',
35949 if(this.bgimage.length){
35952 cls: 'roo-brick-image-view',
35960 initEvents: function()
35962 if(this.title.length || this.html.length){
35963 this.el.on('mouseenter' ,this.enter, this);
35964 this.el.on('mouseleave', this.leave, this);
35967 Roo.EventManager.onWindowResize(this.resize, this);
35969 if(this.bgimage.length){
35970 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35971 this.imageEl.on('load', this.onImageLoad, this);
35978 onImageLoad : function()
35983 resize : function()
35985 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35987 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35989 if(this.bgimage.length){
35990 var image = this.el.select('.roo-brick-image-view', true).first();
35992 image.setWidth(paragraph.getWidth());
35995 image.setHeight(paragraph.getWidth());
35998 this.el.setHeight(image.getHeight());
35999 paragraph.setHeight(image.getHeight());
36005 enter: function(e, el)
36007 e.preventDefault();
36009 if(this.bgimage.length){
36010 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36011 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36015 leave: function(e, el)
36017 e.preventDefault();
36019 if(this.bgimage.length){
36020 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36021 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36036 * @class Roo.bootstrap.NumberField
36037 * @extends Roo.bootstrap.Input
36038 * Bootstrap NumberField class
36044 * Create a new NumberField
36045 * @param {Object} config The config object
36048 Roo.bootstrap.NumberField = function(config){
36049 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36052 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36055 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36057 allowDecimals : true,
36059 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36061 decimalSeparator : ".",
36063 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36065 decimalPrecision : 2,
36067 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36069 allowNegative : true,
36072 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36076 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36078 minValue : Number.NEGATIVE_INFINITY,
36080 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36082 maxValue : Number.MAX_VALUE,
36084 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36086 minText : "The minimum value for this field is {0}",
36088 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36090 maxText : "The maximum value for this field is {0}",
36092 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36093 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36095 nanText : "{0} is not a valid number",
36097 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36099 thousandsDelimiter : false,
36101 * @cfg {String} valueAlign alignment of value
36103 valueAlign : "left",
36105 getAutoCreate : function()
36107 var hiddenInput = {
36111 cls: 'hidden-number-input'
36115 hiddenInput.name = this.name;
36120 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36122 this.name = hiddenInput.name;
36124 if(cfg.cn.length > 0) {
36125 cfg.cn.push(hiddenInput);
36132 initEvents : function()
36134 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36136 var allowed = "0123456789";
36138 if(this.allowDecimals){
36139 allowed += this.decimalSeparator;
36142 if(this.allowNegative){
36146 if(this.thousandsDelimiter) {
36150 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36152 var keyPress = function(e){
36154 var k = e.getKey();
36156 var c = e.getCharCode();
36159 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36160 allowed.indexOf(String.fromCharCode(c)) === -1
36166 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36170 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36175 this.el.on("keypress", keyPress, this);
36178 validateValue : function(value)
36181 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36185 var num = this.parseValue(value);
36188 this.markInvalid(String.format(this.nanText, value));
36192 if(num < this.minValue){
36193 this.markInvalid(String.format(this.minText, this.minValue));
36197 if(num > this.maxValue){
36198 this.markInvalid(String.format(this.maxText, this.maxValue));
36205 getValue : function()
36207 var v = this.hiddenEl().getValue();
36209 return this.fixPrecision(this.parseValue(v));
36212 parseValue : function(value)
36214 if(this.thousandsDelimiter) {
36216 r = new RegExp(",", "g");
36217 value = value.replace(r, "");
36220 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36221 return isNaN(value) ? '' : value;
36224 fixPrecision : function(value)
36226 if(this.thousandsDelimiter) {
36228 r = new RegExp(",", "g");
36229 value = value.replace(r, "");
36232 var nan = isNaN(value);
36234 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36235 return nan ? '' : value;
36237 return parseFloat(value).toFixed(this.decimalPrecision);
36240 setValue : function(v)
36242 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36248 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36250 this.inputEl().dom.value = (v == '') ? '' :
36251 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36253 if(!this.allowZero && v === '0') {
36254 this.hiddenEl().dom.value = '';
36255 this.inputEl().dom.value = '';
36262 decimalPrecisionFcn : function(v)
36264 return Math.floor(v);
36267 beforeBlur : function()
36269 var v = this.parseValue(this.getRawValue());
36271 if(v || v === 0 || v === ''){
36276 hiddenEl : function()
36278 return this.el.select('input.hidden-number-input',true).first();
36290 * @class Roo.bootstrap.DocumentSlider
36291 * @extends Roo.bootstrap.Component
36292 * Bootstrap DocumentSlider class
36295 * Create a new DocumentViewer
36296 * @param {Object} config The config object
36299 Roo.bootstrap.DocumentSlider = function(config){
36300 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36307 * Fire after initEvent
36308 * @param {Roo.bootstrap.DocumentSlider} this
36313 * Fire after update
36314 * @param {Roo.bootstrap.DocumentSlider} this
36320 * @param {Roo.bootstrap.DocumentSlider} this
36326 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36332 getAutoCreate : function()
36336 cls : 'roo-document-slider',
36340 cls : 'roo-document-slider-header',
36344 cls : 'roo-document-slider-header-title'
36350 cls : 'roo-document-slider-body',
36354 cls : 'roo-document-slider-prev',
36358 cls : 'fa fa-chevron-left'
36364 cls : 'roo-document-slider-thumb',
36368 cls : 'roo-document-slider-image'
36374 cls : 'roo-document-slider-next',
36378 cls : 'fa fa-chevron-right'
36390 initEvents : function()
36392 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36393 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36395 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36396 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36398 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36399 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36401 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36402 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36404 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36405 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36407 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36408 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36410 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36411 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36413 this.thumbEl.on('click', this.onClick, this);
36415 this.prevIndicator.on('click', this.prev, this);
36417 this.nextIndicator.on('click', this.next, this);
36421 initial : function()
36423 if(this.files.length){
36424 this.indicator = 1;
36428 this.fireEvent('initial', this);
36431 update : function()
36433 this.imageEl.attr('src', this.files[this.indicator - 1]);
36435 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36437 this.prevIndicator.show();
36439 if(this.indicator == 1){
36440 this.prevIndicator.hide();
36443 this.nextIndicator.show();
36445 if(this.indicator == this.files.length){
36446 this.nextIndicator.hide();
36449 this.thumbEl.scrollTo('top');
36451 this.fireEvent('update', this);
36454 onClick : function(e)
36456 e.preventDefault();
36458 this.fireEvent('click', this);
36463 e.preventDefault();
36465 this.indicator = Math.max(1, this.indicator - 1);
36472 e.preventDefault();
36474 this.indicator = Math.min(this.files.length, this.indicator + 1);
36488 * @class Roo.bootstrap.RadioSet
36489 * @extends Roo.bootstrap.Input
36490 * Bootstrap RadioSet class
36491 * @cfg {String} indicatorpos (left|right) default left
36492 * @cfg {Boolean} inline (true|false) inline the element (default true)
36493 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36495 * Create a new RadioSet
36496 * @param {Object} config The config object
36499 Roo.bootstrap.RadioSet = function(config){
36501 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36505 Roo.bootstrap.RadioSet.register(this);
36510 * Fires when the element is checked or unchecked.
36511 * @param {Roo.bootstrap.RadioSet} this This radio
36512 * @param {Roo.bootstrap.Radio} item The checked item
36517 * Fires when the element is click.
36518 * @param {Roo.bootstrap.RadioSet} this This radio set
36519 * @param {Roo.bootstrap.Radio} item The checked item
36520 * @param {Roo.EventObject} e The event object
36527 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36535 indicatorpos : 'left',
36537 getAutoCreate : function()
36541 cls : 'roo-radio-set-label',
36545 html : this.fieldLabel
36549 if (Roo.bootstrap.version == 3) {
36552 if(this.indicatorpos == 'left'){
36555 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36556 tooltip : 'This field is required'
36561 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36562 tooltip : 'This field is required'
36568 cls : 'roo-radio-set-items'
36571 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36573 if (align === 'left' && this.fieldLabel.length) {
36576 cls : "roo-radio-set-right",
36582 if(this.labelWidth > 12){
36583 label.style = "width: " + this.labelWidth + 'px';
36586 if(this.labelWidth < 13 && this.labelmd == 0){
36587 this.labelmd = this.labelWidth;
36590 if(this.labellg > 0){
36591 label.cls += ' col-lg-' + this.labellg;
36592 items.cls += ' col-lg-' + (12 - this.labellg);
36595 if(this.labelmd > 0){
36596 label.cls += ' col-md-' + this.labelmd;
36597 items.cls += ' col-md-' + (12 - this.labelmd);
36600 if(this.labelsm > 0){
36601 label.cls += ' col-sm-' + this.labelsm;
36602 items.cls += ' col-sm-' + (12 - this.labelsm);
36605 if(this.labelxs > 0){
36606 label.cls += ' col-xs-' + this.labelxs;
36607 items.cls += ' col-xs-' + (12 - this.labelxs);
36613 cls : 'roo-radio-set',
36617 cls : 'roo-radio-set-input',
36620 value : this.value ? this.value : ''
36627 if(this.weight.length){
36628 cfg.cls += ' roo-radio-' + this.weight;
36632 cfg.cls += ' roo-radio-set-inline';
36636 ['xs','sm','md','lg'].map(function(size){
36637 if (settings[size]) {
36638 cfg.cls += ' col-' + size + '-' + settings[size];
36646 initEvents : function()
36648 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36649 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36651 if(!this.fieldLabel.length){
36652 this.labelEl.hide();
36655 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36656 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36658 this.indicator = this.indicatorEl();
36660 if(this.indicator){
36661 this.indicator.addClass('invisible');
36664 this.originalValue = this.getValue();
36668 inputEl: function ()
36670 return this.el.select('.roo-radio-set-input', true).first();
36673 getChildContainer : function()
36675 return this.itemsEl;
36678 register : function(item)
36680 this.radioes.push(item);
36684 validate : function()
36686 if(this.getVisibilityEl().hasClass('hidden')){
36692 Roo.each(this.radioes, function(i){
36701 if(this.allowBlank) {
36705 if(this.disabled || valid){
36710 this.markInvalid();
36715 markValid : function()
36717 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36718 this.indicatorEl().removeClass('visible');
36719 this.indicatorEl().addClass('invisible');
36723 if (Roo.bootstrap.version == 3) {
36724 this.el.removeClass([this.invalidClass, this.validClass]);
36725 this.el.addClass(this.validClass);
36727 this.el.removeClass(['is-invalid','is-valid']);
36728 this.el.addClass(['is-valid']);
36730 this.fireEvent('valid', this);
36733 markInvalid : function(msg)
36735 if(this.allowBlank || this.disabled){
36739 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36740 this.indicatorEl().removeClass('invisible');
36741 this.indicatorEl().addClass('visible');
36743 if (Roo.bootstrap.version == 3) {
36744 this.el.removeClass([this.invalidClass, this.validClass]);
36745 this.el.addClass(this.invalidClass);
36747 this.el.removeClass(['is-invalid','is-valid']);
36748 this.el.addClass(['is-invalid']);
36751 this.fireEvent('invalid', this, msg);
36755 setValue : function(v, suppressEvent)
36757 if(this.value === v){
36764 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36767 Roo.each(this.radioes, function(i){
36769 i.el.removeClass('checked');
36772 Roo.each(this.radioes, function(i){
36774 if(i.value === v || i.value.toString() === v.toString()){
36776 i.el.addClass('checked');
36778 if(suppressEvent !== true){
36779 this.fireEvent('check', this, i);
36790 clearInvalid : function(){
36792 if(!this.el || this.preventMark){
36796 this.el.removeClass([this.invalidClass]);
36798 this.fireEvent('valid', this);
36803 Roo.apply(Roo.bootstrap.RadioSet, {
36807 register : function(set)
36809 this.groups[set.name] = set;
36812 get: function(name)
36814 if (typeof(this.groups[name]) == 'undefined') {
36818 return this.groups[name] ;
36824 * Ext JS Library 1.1.1
36825 * Copyright(c) 2006-2007, Ext JS, LLC.
36827 * Originally Released Under LGPL - original licence link has changed is not relivant.
36830 * <script type="text/javascript">
36835 * @class Roo.bootstrap.SplitBar
36836 * @extends Roo.util.Observable
36837 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36841 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36842 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36843 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36844 split.minSize = 100;
36845 split.maxSize = 600;
36846 split.animate = true;
36847 split.on('moved', splitterMoved);
36850 * Create a new SplitBar
36851 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36852 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36853 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36854 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36855 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36856 position of the SplitBar).
36858 Roo.bootstrap.SplitBar = function(cfg){
36863 // dragElement : elm
36864 // resizingElement: el,
36866 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36867 // placement : Roo.bootstrap.SplitBar.LEFT ,
36868 // existingProxy ???
36871 this.el = Roo.get(cfg.dragElement, true);
36872 this.el.dom.unselectable = "on";
36874 this.resizingEl = Roo.get(cfg.resizingElement, true);
36878 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36879 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36882 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36885 * The minimum size of the resizing element. (Defaults to 0)
36891 * The maximum size of the resizing element. (Defaults to 2000)
36894 this.maxSize = 2000;
36897 * Whether to animate the transition to the new size
36900 this.animate = false;
36903 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36906 this.useShim = false;
36911 if(!cfg.existingProxy){
36913 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36915 this.proxy = Roo.get(cfg.existingProxy).dom;
36918 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36921 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36924 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36927 this.dragSpecs = {};
36930 * @private The adapter to use to positon and resize elements
36932 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36933 this.adapter.init(this);
36935 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36937 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36938 this.el.addClass("roo-splitbar-h");
36941 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36942 this.el.addClass("roo-splitbar-v");
36948 * Fires when the splitter is moved (alias for {@link #event-moved})
36949 * @param {Roo.bootstrap.SplitBar} this
36950 * @param {Number} newSize the new width or height
36955 * Fires when the splitter is moved
36956 * @param {Roo.bootstrap.SplitBar} this
36957 * @param {Number} newSize the new width or height
36961 * @event beforeresize
36962 * Fires before the splitter is dragged
36963 * @param {Roo.bootstrap.SplitBar} this
36965 "beforeresize" : true,
36967 "beforeapply" : true
36970 Roo.util.Observable.call(this);
36973 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36974 onStartProxyDrag : function(x, y){
36975 this.fireEvent("beforeresize", this);
36977 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36979 o.enableDisplayMode("block");
36980 // all splitbars share the same overlay
36981 Roo.bootstrap.SplitBar.prototype.overlay = o;
36983 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36984 this.overlay.show();
36985 Roo.get(this.proxy).setDisplayed("block");
36986 var size = this.adapter.getElementSize(this);
36987 this.activeMinSize = this.getMinimumSize();;
36988 this.activeMaxSize = this.getMaximumSize();;
36989 var c1 = size - this.activeMinSize;
36990 var c2 = Math.max(this.activeMaxSize - size, 0);
36991 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36992 this.dd.resetConstraints();
36993 this.dd.setXConstraint(
36994 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36995 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36997 this.dd.setYConstraint(0, 0);
36999 this.dd.resetConstraints();
37000 this.dd.setXConstraint(0, 0);
37001 this.dd.setYConstraint(
37002 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37003 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37006 this.dragSpecs.startSize = size;
37007 this.dragSpecs.startPoint = [x, y];
37008 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37012 * @private Called after the drag operation by the DDProxy
37014 onEndProxyDrag : function(e){
37015 Roo.get(this.proxy).setDisplayed(false);
37016 var endPoint = Roo.lib.Event.getXY(e);
37018 this.overlay.hide();
37021 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37022 newSize = this.dragSpecs.startSize +
37023 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37024 endPoint[0] - this.dragSpecs.startPoint[0] :
37025 this.dragSpecs.startPoint[0] - endPoint[0]
37028 newSize = this.dragSpecs.startSize +
37029 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37030 endPoint[1] - this.dragSpecs.startPoint[1] :
37031 this.dragSpecs.startPoint[1] - endPoint[1]
37034 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37035 if(newSize != this.dragSpecs.startSize){
37036 if(this.fireEvent('beforeapply', this, newSize) !== false){
37037 this.adapter.setElementSize(this, newSize);
37038 this.fireEvent("moved", this, newSize);
37039 this.fireEvent("resize", this, newSize);
37045 * Get the adapter this SplitBar uses
37046 * @return The adapter object
37048 getAdapter : function(){
37049 return this.adapter;
37053 * Set the adapter this SplitBar uses
37054 * @param {Object} adapter A SplitBar adapter object
37056 setAdapter : function(adapter){
37057 this.adapter = adapter;
37058 this.adapter.init(this);
37062 * Gets the minimum size for the resizing element
37063 * @return {Number} The minimum size
37065 getMinimumSize : function(){
37066 return this.minSize;
37070 * Sets the minimum size for the resizing element
37071 * @param {Number} minSize The minimum size
37073 setMinimumSize : function(minSize){
37074 this.minSize = minSize;
37078 * Gets the maximum size for the resizing element
37079 * @return {Number} The maximum size
37081 getMaximumSize : function(){
37082 return this.maxSize;
37086 * Sets the maximum size for the resizing element
37087 * @param {Number} maxSize The maximum size
37089 setMaximumSize : function(maxSize){
37090 this.maxSize = maxSize;
37094 * Sets the initialize size for the resizing element
37095 * @param {Number} size The initial size
37097 setCurrentSize : function(size){
37098 var oldAnimate = this.animate;
37099 this.animate = false;
37100 this.adapter.setElementSize(this, size);
37101 this.animate = oldAnimate;
37105 * Destroy this splitbar.
37106 * @param {Boolean} removeEl True to remove the element
37108 destroy : function(removeEl){
37110 this.shim.remove();
37113 this.proxy.parentNode.removeChild(this.proxy);
37121 * @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.
37123 Roo.bootstrap.SplitBar.createProxy = function(dir){
37124 var proxy = new Roo.Element(document.createElement("div"));
37125 proxy.unselectable();
37126 var cls = 'roo-splitbar-proxy';
37127 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37128 document.body.appendChild(proxy.dom);
37133 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37134 * Default Adapter. It assumes the splitter and resizing element are not positioned
37135 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37137 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37140 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37141 // do nothing for now
37142 init : function(s){
37146 * Called before drag operations to get the current size of the resizing element.
37147 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37149 getElementSize : function(s){
37150 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37151 return s.resizingEl.getWidth();
37153 return s.resizingEl.getHeight();
37158 * Called after drag operations to set the size of the resizing element.
37159 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37160 * @param {Number} newSize The new size to set
37161 * @param {Function} onComplete A function to be invoked when resizing is complete
37163 setElementSize : function(s, newSize, onComplete){
37164 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37166 s.resizingEl.setWidth(newSize);
37168 onComplete(s, newSize);
37171 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37176 s.resizingEl.setHeight(newSize);
37178 onComplete(s, newSize);
37181 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37188 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37189 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37190 * Adapter that moves the splitter element to align with the resized sizing element.
37191 * Used with an absolute positioned SplitBar.
37192 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37193 * document.body, make sure you assign an id to the body element.
37195 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37196 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37197 this.container = Roo.get(container);
37200 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37201 init : function(s){
37202 this.basic.init(s);
37205 getElementSize : function(s){
37206 return this.basic.getElementSize(s);
37209 setElementSize : function(s, newSize, onComplete){
37210 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37213 moveSplitter : function(s){
37214 var yes = Roo.bootstrap.SplitBar;
37215 switch(s.placement){
37217 s.el.setX(s.resizingEl.getRight());
37220 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37223 s.el.setY(s.resizingEl.getBottom());
37226 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37233 * Orientation constant - Create a vertical SplitBar
37237 Roo.bootstrap.SplitBar.VERTICAL = 1;
37240 * Orientation constant - Create a horizontal SplitBar
37244 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37247 * Placement constant - The resizing element is to the left of the splitter element
37251 Roo.bootstrap.SplitBar.LEFT = 1;
37254 * Placement constant - The resizing element is to the right of the splitter element
37258 Roo.bootstrap.SplitBar.RIGHT = 2;
37261 * Placement constant - The resizing element is positioned above the splitter element
37265 Roo.bootstrap.SplitBar.TOP = 3;
37268 * Placement constant - The resizing element is positioned under splitter element
37272 Roo.bootstrap.SplitBar.BOTTOM = 4;
37273 Roo.namespace("Roo.bootstrap.layout");/*
37275 * Ext JS Library 1.1.1
37276 * Copyright(c) 2006-2007, Ext JS, LLC.
37278 * Originally Released Under LGPL - original licence link has changed is not relivant.
37281 * <script type="text/javascript">
37285 * @class Roo.bootstrap.layout.Manager
37286 * @extends Roo.bootstrap.Component
37287 * Base class for layout managers.
37289 Roo.bootstrap.layout.Manager = function(config)
37291 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37297 /** false to disable window resize monitoring @type Boolean */
37298 this.monitorWindowResize = true;
37303 * Fires when a layout is performed.
37304 * @param {Roo.LayoutManager} this
37308 * @event regionresized
37309 * Fires when the user resizes a region.
37310 * @param {Roo.LayoutRegion} region The resized region
37311 * @param {Number} newSize The new size (width for east/west, height for north/south)
37313 "regionresized" : true,
37315 * @event regioncollapsed
37316 * Fires when a region is collapsed.
37317 * @param {Roo.LayoutRegion} region The collapsed region
37319 "regioncollapsed" : true,
37321 * @event regionexpanded
37322 * Fires when a region is expanded.
37323 * @param {Roo.LayoutRegion} region The expanded region
37325 "regionexpanded" : true
37327 this.updating = false;
37330 this.el = Roo.get(config.el);
37336 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37341 monitorWindowResize : true,
37347 onRender : function(ct, position)
37350 this.el = Roo.get(ct);
37353 //this.fireEvent('render',this);
37357 initEvents: function()
37361 // ie scrollbar fix
37362 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37363 document.body.scroll = "no";
37364 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37365 this.el.position('relative');
37367 this.id = this.el.id;
37368 this.el.addClass("roo-layout-container");
37369 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37370 if(this.el.dom != document.body ) {
37371 this.el.on('resize', this.layout,this);
37372 this.el.on('show', this.layout,this);
37378 * Returns true if this layout is currently being updated
37379 * @return {Boolean}
37381 isUpdating : function(){
37382 return this.updating;
37386 * Suspend the LayoutManager from doing auto-layouts while
37387 * making multiple add or remove calls
37389 beginUpdate : function(){
37390 this.updating = true;
37394 * Restore auto-layouts and optionally disable the manager from performing a layout
37395 * @param {Boolean} noLayout true to disable a layout update
37397 endUpdate : function(noLayout){
37398 this.updating = false;
37404 layout: function(){
37408 onRegionResized : function(region, newSize){
37409 this.fireEvent("regionresized", region, newSize);
37413 onRegionCollapsed : function(region){
37414 this.fireEvent("regioncollapsed", region);
37417 onRegionExpanded : function(region){
37418 this.fireEvent("regionexpanded", region);
37422 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37423 * performs box-model adjustments.
37424 * @return {Object} The size as an object {width: (the width), height: (the height)}
37426 getViewSize : function()
37429 if(this.el.dom != document.body){
37430 size = this.el.getSize();
37432 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37434 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37435 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37440 * Returns the Element this layout is bound to.
37441 * @return {Roo.Element}
37443 getEl : function(){
37448 * Returns the specified region.
37449 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37450 * @return {Roo.LayoutRegion}
37452 getRegion : function(target){
37453 return this.regions[target.toLowerCase()];
37456 onWindowResize : function(){
37457 if(this.monitorWindowResize){
37464 * Ext JS Library 1.1.1
37465 * Copyright(c) 2006-2007, Ext JS, LLC.
37467 * Originally Released Under LGPL - original licence link has changed is not relivant.
37470 * <script type="text/javascript">
37473 * @class Roo.bootstrap.layout.Border
37474 * @extends Roo.bootstrap.layout.Manager
37475 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37476 * please see: examples/bootstrap/nested.html<br><br>
37478 <b>The container the layout is rendered into can be either the body element or any other element.
37479 If it is not the body element, the container needs to either be an absolute positioned element,
37480 or you will need to add "position:relative" to the css of the container. You will also need to specify
37481 the container size if it is not the body element.</b>
37484 * Create a new Border
37485 * @param {Object} config Configuration options
37487 Roo.bootstrap.layout.Border = function(config){
37488 config = config || {};
37489 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37493 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37494 if(config[region]){
37495 config[region].region = region;
37496 this.addRegion(config[region]);
37502 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37504 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37506 parent : false, // this might point to a 'nest' or a ???
37509 * Creates and adds a new region if it doesn't already exist.
37510 * @param {String} target The target region key (north, south, east, west or center).
37511 * @param {Object} config The regions config object
37512 * @return {BorderLayoutRegion} The new region
37514 addRegion : function(config)
37516 if(!this.regions[config.region]){
37517 var r = this.factory(config);
37518 this.bindRegion(r);
37520 return this.regions[config.region];
37524 bindRegion : function(r){
37525 this.regions[r.config.region] = r;
37527 r.on("visibilitychange", this.layout, this);
37528 r.on("paneladded", this.layout, this);
37529 r.on("panelremoved", this.layout, this);
37530 r.on("invalidated", this.layout, this);
37531 r.on("resized", this.onRegionResized, this);
37532 r.on("collapsed", this.onRegionCollapsed, this);
37533 r.on("expanded", this.onRegionExpanded, this);
37537 * Performs a layout update.
37539 layout : function()
37541 if(this.updating) {
37545 // render all the rebions if they have not been done alreayd?
37546 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37547 if(this.regions[region] && !this.regions[region].bodyEl){
37548 this.regions[region].onRender(this.el)
37552 var size = this.getViewSize();
37553 var w = size.width;
37554 var h = size.height;
37559 //var x = 0, y = 0;
37561 var rs = this.regions;
37562 var north = rs["north"];
37563 var south = rs["south"];
37564 var west = rs["west"];
37565 var east = rs["east"];
37566 var center = rs["center"];
37567 //if(this.hideOnLayout){ // not supported anymore
37568 //c.el.setStyle("display", "none");
37570 if(north && north.isVisible()){
37571 var b = north.getBox();
37572 var m = north.getMargins();
37573 b.width = w - (m.left+m.right);
37576 centerY = b.height + b.y + m.bottom;
37577 centerH -= centerY;
37578 north.updateBox(this.safeBox(b));
37580 if(south && south.isVisible()){
37581 var b = south.getBox();
37582 var m = south.getMargins();
37583 b.width = w - (m.left+m.right);
37585 var totalHeight = (b.height + m.top + m.bottom);
37586 b.y = h - totalHeight + m.top;
37587 centerH -= totalHeight;
37588 south.updateBox(this.safeBox(b));
37590 if(west && west.isVisible()){
37591 var b = west.getBox();
37592 var m = west.getMargins();
37593 b.height = centerH - (m.top+m.bottom);
37595 b.y = centerY + m.top;
37596 var totalWidth = (b.width + m.left + m.right);
37597 centerX += totalWidth;
37598 centerW -= totalWidth;
37599 west.updateBox(this.safeBox(b));
37601 if(east && east.isVisible()){
37602 var b = east.getBox();
37603 var m = east.getMargins();
37604 b.height = centerH - (m.top+m.bottom);
37605 var totalWidth = (b.width + m.left + m.right);
37606 b.x = w - totalWidth + m.left;
37607 b.y = centerY + m.top;
37608 centerW -= totalWidth;
37609 east.updateBox(this.safeBox(b));
37612 var m = center.getMargins();
37614 x: centerX + m.left,
37615 y: centerY + m.top,
37616 width: centerW - (m.left+m.right),
37617 height: centerH - (m.top+m.bottom)
37619 //if(this.hideOnLayout){
37620 //center.el.setStyle("display", "block");
37622 center.updateBox(this.safeBox(centerBox));
37625 this.fireEvent("layout", this);
37629 safeBox : function(box){
37630 box.width = Math.max(0, box.width);
37631 box.height = Math.max(0, box.height);
37636 * Adds a ContentPanel (or subclass) to this layout.
37637 * @param {String} target The target region key (north, south, east, west or center).
37638 * @param {Roo.ContentPanel} panel The panel to add
37639 * @return {Roo.ContentPanel} The added panel
37641 add : function(target, panel){
37643 target = target.toLowerCase();
37644 return this.regions[target].add(panel);
37648 * Remove a ContentPanel (or subclass) to this layout.
37649 * @param {String} target The target region key (north, south, east, west or center).
37650 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37651 * @return {Roo.ContentPanel} The removed panel
37653 remove : function(target, panel){
37654 target = target.toLowerCase();
37655 return this.regions[target].remove(panel);
37659 * Searches all regions for a panel with the specified id
37660 * @param {String} panelId
37661 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37663 findPanel : function(panelId){
37664 var rs = this.regions;
37665 for(var target in rs){
37666 if(typeof rs[target] != "function"){
37667 var p = rs[target].getPanel(panelId);
37677 * Searches all regions for a panel with the specified id and activates (shows) it.
37678 * @param {String/ContentPanel} panelId The panels id or the panel itself
37679 * @return {Roo.ContentPanel} The shown panel or null
37681 showPanel : function(panelId) {
37682 var rs = this.regions;
37683 for(var target in rs){
37684 var r = rs[target];
37685 if(typeof r != "function"){
37686 if(r.hasPanel(panelId)){
37687 return r.showPanel(panelId);
37695 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37696 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37699 restoreState : function(provider){
37701 provider = Roo.state.Manager;
37703 var sm = new Roo.LayoutStateManager();
37704 sm.init(this, provider);
37710 * Adds a xtype elements to the layout.
37714 xtype : 'ContentPanel',
37721 xtype : 'NestedLayoutPanel',
37727 items : [ ... list of content panels or nested layout panels.. ]
37731 * @param {Object} cfg Xtype definition of item to add.
37733 addxtype : function(cfg)
37735 // basically accepts a pannel...
37736 // can accept a layout region..!?!?
37737 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37740 // theory? children can only be panels??
37742 //if (!cfg.xtype.match(/Panel$/)) {
37747 if (typeof(cfg.region) == 'undefined') {
37748 Roo.log("Failed to add Panel, region was not set");
37752 var region = cfg.region;
37758 xitems = cfg.items;
37763 if ( region == 'center') {
37764 Roo.log("Center: " + cfg.title);
37770 case 'Content': // ContentPanel (el, cfg)
37771 case 'Scroll': // ContentPanel (el, cfg)
37773 cfg.autoCreate = cfg.autoCreate || true;
37774 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37776 // var el = this.el.createChild();
37777 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37780 this.add(region, ret);
37784 case 'TreePanel': // our new panel!
37785 cfg.el = this.el.createChild();
37786 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37787 this.add(region, ret);
37792 // create a new Layout (which is a Border Layout...
37794 var clayout = cfg.layout;
37795 clayout.el = this.el.createChild();
37796 clayout.items = clayout.items || [];
37800 // replace this exitems with the clayout ones..
37801 xitems = clayout.items;
37803 // force background off if it's in center...
37804 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37805 cfg.background = false;
37807 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37810 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37811 //console.log('adding nested layout panel ' + cfg.toSource());
37812 this.add(region, ret);
37813 nb = {}; /// find first...
37818 // needs grid and region
37820 //var el = this.getRegion(region).el.createChild();
37822 *var el = this.el.createChild();
37823 // create the grid first...
37824 cfg.grid.container = el;
37825 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37828 if (region == 'center' && this.active ) {
37829 cfg.background = false;
37832 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37834 this.add(region, ret);
37836 if (cfg.background) {
37837 // render grid on panel activation (if panel background)
37838 ret.on('activate', function(gp) {
37839 if (!gp.grid.rendered) {
37840 // gp.grid.render(el);
37844 // cfg.grid.render(el);
37850 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37851 // it was the old xcomponent building that caused this before.
37852 // espeically if border is the top element in the tree.
37862 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37864 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37865 this.add(region, ret);
37869 throw "Can not add '" + cfg.xtype + "' to Border";
37875 this.beginUpdate();
37879 Roo.each(xitems, function(i) {
37880 region = nb && i.region ? i.region : false;
37882 var add = ret.addxtype(i);
37885 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37886 if (!i.background) {
37887 abn[region] = nb[region] ;
37894 // make the last non-background panel active..
37895 //if (nb) { Roo.log(abn); }
37898 for(var r in abn) {
37899 region = this.getRegion(r);
37901 // tried using nb[r], but it does not work..
37903 region.showPanel(abn[r]);
37914 factory : function(cfg)
37917 var validRegions = Roo.bootstrap.layout.Border.regions;
37919 var target = cfg.region;
37922 var r = Roo.bootstrap.layout;
37926 return new r.North(cfg);
37928 return new r.South(cfg);
37930 return new r.East(cfg);
37932 return new r.West(cfg);
37934 return new r.Center(cfg);
37936 throw 'Layout region "'+target+'" not supported.';
37943 * Ext JS Library 1.1.1
37944 * Copyright(c) 2006-2007, Ext JS, LLC.
37946 * Originally Released Under LGPL - original licence link has changed is not relivant.
37949 * <script type="text/javascript">
37953 * @class Roo.bootstrap.layout.Basic
37954 * @extends Roo.util.Observable
37955 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37956 * and does not have a titlebar, tabs or any other features. All it does is size and position
37957 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37958 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37959 * @cfg {string} region the region that it inhabits..
37960 * @cfg {bool} skipConfig skip config?
37964 Roo.bootstrap.layout.Basic = function(config){
37966 this.mgr = config.mgr;
37968 this.position = config.region;
37970 var skipConfig = config.skipConfig;
37974 * @scope Roo.BasicLayoutRegion
37978 * @event beforeremove
37979 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37980 * @param {Roo.LayoutRegion} this
37981 * @param {Roo.ContentPanel} panel The panel
37982 * @param {Object} e The cancel event object
37984 "beforeremove" : true,
37986 * @event invalidated
37987 * Fires when the layout for this region is changed.
37988 * @param {Roo.LayoutRegion} this
37990 "invalidated" : true,
37992 * @event visibilitychange
37993 * Fires when this region is shown or hidden
37994 * @param {Roo.LayoutRegion} this
37995 * @param {Boolean} visibility true or false
37997 "visibilitychange" : true,
37999 * @event paneladded
38000 * Fires when a panel is added.
38001 * @param {Roo.LayoutRegion} this
38002 * @param {Roo.ContentPanel} panel The panel
38004 "paneladded" : true,
38006 * @event panelremoved
38007 * Fires when a panel is removed.
38008 * @param {Roo.LayoutRegion} this
38009 * @param {Roo.ContentPanel} panel The panel
38011 "panelremoved" : true,
38013 * @event beforecollapse
38014 * Fires when this region before collapse.
38015 * @param {Roo.LayoutRegion} this
38017 "beforecollapse" : true,
38020 * Fires when this region is collapsed.
38021 * @param {Roo.LayoutRegion} this
38023 "collapsed" : true,
38026 * Fires when this region is expanded.
38027 * @param {Roo.LayoutRegion} this
38032 * Fires when this region is slid into view.
38033 * @param {Roo.LayoutRegion} this
38035 "slideshow" : true,
38038 * Fires when this region slides out of view.
38039 * @param {Roo.LayoutRegion} this
38041 "slidehide" : true,
38043 * @event panelactivated
38044 * Fires when a panel is activated.
38045 * @param {Roo.LayoutRegion} this
38046 * @param {Roo.ContentPanel} panel The activated panel
38048 "panelactivated" : true,
38051 * Fires when the user resizes this region.
38052 * @param {Roo.LayoutRegion} this
38053 * @param {Number} newSize The new size (width for east/west, height for north/south)
38057 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38058 this.panels = new Roo.util.MixedCollection();
38059 this.panels.getKey = this.getPanelId.createDelegate(this);
38061 this.activePanel = null;
38062 // ensure listeners are added...
38064 if (config.listeners || config.events) {
38065 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38066 listeners : config.listeners || {},
38067 events : config.events || {}
38071 if(skipConfig !== true){
38072 this.applyConfig(config);
38076 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38078 getPanelId : function(p){
38082 applyConfig : function(config){
38083 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38084 this.config = config;
38089 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38090 * the width, for horizontal (north, south) the height.
38091 * @param {Number} newSize The new width or height
38093 resizeTo : function(newSize){
38094 var el = this.el ? this.el :
38095 (this.activePanel ? this.activePanel.getEl() : null);
38097 switch(this.position){
38100 el.setWidth(newSize);
38101 this.fireEvent("resized", this, newSize);
38105 el.setHeight(newSize);
38106 this.fireEvent("resized", this, newSize);
38112 getBox : function(){
38113 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38116 getMargins : function(){
38117 return this.margins;
38120 updateBox : function(box){
38122 var el = this.activePanel.getEl();
38123 el.dom.style.left = box.x + "px";
38124 el.dom.style.top = box.y + "px";
38125 this.activePanel.setSize(box.width, box.height);
38129 * Returns the container element for this region.
38130 * @return {Roo.Element}
38132 getEl : function(){
38133 return this.activePanel;
38137 * Returns true if this region is currently visible.
38138 * @return {Boolean}
38140 isVisible : function(){
38141 return this.activePanel ? true : false;
38144 setActivePanel : function(panel){
38145 panel = this.getPanel(panel);
38146 if(this.activePanel && this.activePanel != panel){
38147 this.activePanel.setActiveState(false);
38148 this.activePanel.getEl().setLeftTop(-10000,-10000);
38150 this.activePanel = panel;
38151 panel.setActiveState(true);
38153 panel.setSize(this.box.width, this.box.height);
38155 this.fireEvent("panelactivated", this, panel);
38156 this.fireEvent("invalidated");
38160 * Show the specified panel.
38161 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38162 * @return {Roo.ContentPanel} The shown panel or null
38164 showPanel : function(panel){
38165 panel = this.getPanel(panel);
38167 this.setActivePanel(panel);
38173 * Get the active panel for this region.
38174 * @return {Roo.ContentPanel} The active panel or null
38176 getActivePanel : function(){
38177 return this.activePanel;
38181 * Add the passed ContentPanel(s)
38182 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38183 * @return {Roo.ContentPanel} The panel added (if only one was added)
38185 add : function(panel){
38186 if(arguments.length > 1){
38187 for(var i = 0, len = arguments.length; i < len; i++) {
38188 this.add(arguments[i]);
38192 if(this.hasPanel(panel)){
38193 this.showPanel(panel);
38196 var el = panel.getEl();
38197 if(el.dom.parentNode != this.mgr.el.dom){
38198 this.mgr.el.dom.appendChild(el.dom);
38200 if(panel.setRegion){
38201 panel.setRegion(this);
38203 this.panels.add(panel);
38204 el.setStyle("position", "absolute");
38205 if(!panel.background){
38206 this.setActivePanel(panel);
38207 if(this.config.initialSize && this.panels.getCount()==1){
38208 this.resizeTo(this.config.initialSize);
38211 this.fireEvent("paneladded", this, panel);
38216 * Returns true if the panel is in this region.
38217 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38218 * @return {Boolean}
38220 hasPanel : function(panel){
38221 if(typeof panel == "object"){ // must be panel obj
38222 panel = panel.getId();
38224 return this.getPanel(panel) ? true : false;
38228 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38229 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38230 * @param {Boolean} preservePanel Overrides the config preservePanel option
38231 * @return {Roo.ContentPanel} The panel that was removed
38233 remove : function(panel, preservePanel){
38234 panel = this.getPanel(panel);
38239 this.fireEvent("beforeremove", this, panel, e);
38240 if(e.cancel === true){
38243 var panelId = panel.getId();
38244 this.panels.removeKey(panelId);
38249 * Returns the panel specified or null if it's not in this region.
38250 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38251 * @return {Roo.ContentPanel}
38253 getPanel : function(id){
38254 if(typeof id == "object"){ // must be panel obj
38257 return this.panels.get(id);
38261 * Returns this regions position (north/south/east/west/center).
38264 getPosition: function(){
38265 return this.position;
38269 * Ext JS Library 1.1.1
38270 * Copyright(c) 2006-2007, Ext JS, LLC.
38272 * Originally Released Under LGPL - original licence link has changed is not relivant.
38275 * <script type="text/javascript">
38279 * @class Roo.bootstrap.layout.Region
38280 * @extends Roo.bootstrap.layout.Basic
38281 * This class represents a region in a layout manager.
38283 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38284 * @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})
38285 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38286 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38287 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38288 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38289 * @cfg {String} title The title for the region (overrides panel titles)
38290 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38291 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38292 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38293 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38294 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38295 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38296 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38297 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38298 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38299 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38301 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38302 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38303 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38304 * @cfg {Number} width For East/West panels
38305 * @cfg {Number} height For North/South panels
38306 * @cfg {Boolean} split To show the splitter
38307 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38309 * @cfg {string} cls Extra CSS classes to add to region
38311 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38312 * @cfg {string} region the region that it inhabits..
38315 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38316 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38318 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38319 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38320 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38322 Roo.bootstrap.layout.Region = function(config)
38324 this.applyConfig(config);
38326 var mgr = config.mgr;
38327 var pos = config.region;
38328 config.skipConfig = true;
38329 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38332 this.onRender(mgr.el);
38335 this.visible = true;
38336 this.collapsed = false;
38337 this.unrendered_panels = [];
38340 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38342 position: '', // set by wrapper (eg. north/south etc..)
38343 unrendered_panels : null, // unrendered panels.
38345 tabPosition : false,
38347 mgr: false, // points to 'Border'
38350 createBody : function(){
38351 /** This region's body element
38352 * @type Roo.Element */
38353 this.bodyEl = this.el.createChild({
38355 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38359 onRender: function(ctr, pos)
38361 var dh = Roo.DomHelper;
38362 /** This region's container element
38363 * @type Roo.Element */
38364 this.el = dh.append(ctr.dom, {
38366 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38368 /** This region's title element
38369 * @type Roo.Element */
38371 this.titleEl = dh.append(this.el.dom, {
38373 unselectable: "on",
38374 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38376 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38377 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38381 this.titleEl.enableDisplayMode();
38382 /** This region's title text element
38383 * @type HTMLElement */
38384 this.titleTextEl = this.titleEl.dom.firstChild;
38385 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38387 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38388 this.closeBtn.enableDisplayMode();
38389 this.closeBtn.on("click", this.closeClicked, this);
38390 this.closeBtn.hide();
38392 this.createBody(this.config);
38393 if(this.config.hideWhenEmpty){
38395 this.on("paneladded", this.validateVisibility, this);
38396 this.on("panelremoved", this.validateVisibility, this);
38398 if(this.autoScroll){
38399 this.bodyEl.setStyle("overflow", "auto");
38401 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38403 //if(c.titlebar !== false){
38404 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38405 this.titleEl.hide();
38407 this.titleEl.show();
38408 if(this.config.title){
38409 this.titleTextEl.innerHTML = this.config.title;
38413 if(this.config.collapsed){
38414 this.collapse(true);
38416 if(this.config.hidden){
38420 if (this.unrendered_panels && this.unrendered_panels.length) {
38421 for (var i =0;i< this.unrendered_panels.length; i++) {
38422 this.add(this.unrendered_panels[i]);
38424 this.unrendered_panels = null;
38430 applyConfig : function(c)
38433 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38434 var dh = Roo.DomHelper;
38435 if(c.titlebar !== false){
38436 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38437 this.collapseBtn.on("click", this.collapse, this);
38438 this.collapseBtn.enableDisplayMode();
38440 if(c.showPin === true || this.showPin){
38441 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38442 this.stickBtn.enableDisplayMode();
38443 this.stickBtn.on("click", this.expand, this);
38444 this.stickBtn.hide();
38449 /** This region's collapsed element
38450 * @type Roo.Element */
38453 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38454 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38457 if(c.floatable !== false){
38458 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38459 this.collapsedEl.on("click", this.collapseClick, this);
38462 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38463 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38464 id: "message", unselectable: "on", style:{"float":"left"}});
38465 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38467 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38468 this.expandBtn.on("click", this.expand, this);
38472 if(this.collapseBtn){
38473 this.collapseBtn.setVisible(c.collapsible == true);
38476 this.cmargins = c.cmargins || this.cmargins ||
38477 (this.position == "west" || this.position == "east" ?
38478 {top: 0, left: 2, right:2, bottom: 0} :
38479 {top: 2, left: 0, right:0, bottom: 2});
38481 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38484 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38486 this.autoScroll = c.autoScroll || false;
38491 this.duration = c.duration || .30;
38492 this.slideDuration = c.slideDuration || .45;
38497 * Returns true if this region is currently visible.
38498 * @return {Boolean}
38500 isVisible : function(){
38501 return this.visible;
38505 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38506 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38508 //setCollapsedTitle : function(title){
38509 // title = title || " ";
38510 // if(this.collapsedTitleTextEl){
38511 // this.collapsedTitleTextEl.innerHTML = title;
38515 getBox : function(){
38517 // if(!this.collapsed){
38518 b = this.el.getBox(false, true);
38520 // b = this.collapsedEl.getBox(false, true);
38525 getMargins : function(){
38526 return this.margins;
38527 //return this.collapsed ? this.cmargins : this.margins;
38530 highlight : function(){
38531 this.el.addClass("x-layout-panel-dragover");
38534 unhighlight : function(){
38535 this.el.removeClass("x-layout-panel-dragover");
38538 updateBox : function(box)
38540 if (!this.bodyEl) {
38541 return; // not rendered yet..
38545 if(!this.collapsed){
38546 this.el.dom.style.left = box.x + "px";
38547 this.el.dom.style.top = box.y + "px";
38548 this.updateBody(box.width, box.height);
38550 this.collapsedEl.dom.style.left = box.x + "px";
38551 this.collapsedEl.dom.style.top = box.y + "px";
38552 this.collapsedEl.setSize(box.width, box.height);
38555 this.tabs.autoSizeTabs();
38559 updateBody : function(w, h)
38562 this.el.setWidth(w);
38563 w -= this.el.getBorderWidth("rl");
38564 if(this.config.adjustments){
38565 w += this.config.adjustments[0];
38568 if(h !== null && h > 0){
38569 this.el.setHeight(h);
38570 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38571 h -= this.el.getBorderWidth("tb");
38572 if(this.config.adjustments){
38573 h += this.config.adjustments[1];
38575 this.bodyEl.setHeight(h);
38577 h = this.tabs.syncHeight(h);
38580 if(this.panelSize){
38581 w = w !== null ? w : this.panelSize.width;
38582 h = h !== null ? h : this.panelSize.height;
38584 if(this.activePanel){
38585 var el = this.activePanel.getEl();
38586 w = w !== null ? w : el.getWidth();
38587 h = h !== null ? h : el.getHeight();
38588 this.panelSize = {width: w, height: h};
38589 this.activePanel.setSize(w, h);
38591 if(Roo.isIE && this.tabs){
38592 this.tabs.el.repaint();
38597 * Returns the container element for this region.
38598 * @return {Roo.Element}
38600 getEl : function(){
38605 * Hides this region.
38608 //if(!this.collapsed){
38609 this.el.dom.style.left = "-2000px";
38612 // this.collapsedEl.dom.style.left = "-2000px";
38613 // this.collapsedEl.hide();
38615 this.visible = false;
38616 this.fireEvent("visibilitychange", this, false);
38620 * Shows this region if it was previously hidden.
38623 //if(!this.collapsed){
38626 // this.collapsedEl.show();
38628 this.visible = true;
38629 this.fireEvent("visibilitychange", this, true);
38632 closeClicked : function(){
38633 if(this.activePanel){
38634 this.remove(this.activePanel);
38638 collapseClick : function(e){
38640 e.stopPropagation();
38643 e.stopPropagation();
38649 * Collapses this region.
38650 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38653 collapse : function(skipAnim, skipCheck = false){
38654 if(this.collapsed) {
38658 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38660 this.collapsed = true;
38662 this.split.el.hide();
38664 if(this.config.animate && skipAnim !== true){
38665 this.fireEvent("invalidated", this);
38666 this.animateCollapse();
38668 this.el.setLocation(-20000,-20000);
38670 this.collapsedEl.show();
38671 this.fireEvent("collapsed", this);
38672 this.fireEvent("invalidated", this);
38678 animateCollapse : function(){
38683 * Expands this region if it was previously collapsed.
38684 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38685 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38688 expand : function(e, skipAnim){
38690 e.stopPropagation();
38692 if(!this.collapsed || this.el.hasActiveFx()) {
38696 this.afterSlideIn();
38699 this.collapsed = false;
38700 if(this.config.animate && skipAnim !== true){
38701 this.animateExpand();
38705 this.split.el.show();
38707 this.collapsedEl.setLocation(-2000,-2000);
38708 this.collapsedEl.hide();
38709 this.fireEvent("invalidated", this);
38710 this.fireEvent("expanded", this);
38714 animateExpand : function(){
38718 initTabs : function()
38720 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38722 var ts = new Roo.bootstrap.panel.Tabs({
38723 el: this.bodyEl.dom,
38725 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38726 disableTooltips: this.config.disableTabTips,
38727 toolbar : this.config.toolbar
38730 if(this.config.hideTabs){
38731 ts.stripWrap.setDisplayed(false);
38734 ts.resizeTabs = this.config.resizeTabs === true;
38735 ts.minTabWidth = this.config.minTabWidth || 40;
38736 ts.maxTabWidth = this.config.maxTabWidth || 250;
38737 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38738 ts.monitorResize = false;
38739 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38740 ts.bodyEl.addClass('roo-layout-tabs-body');
38741 this.panels.each(this.initPanelAsTab, this);
38744 initPanelAsTab : function(panel){
38745 var ti = this.tabs.addTab(
38749 this.config.closeOnTab && panel.isClosable(),
38752 if(panel.tabTip !== undefined){
38753 ti.setTooltip(panel.tabTip);
38755 ti.on("activate", function(){
38756 this.setActivePanel(panel);
38759 if(this.config.closeOnTab){
38760 ti.on("beforeclose", function(t, e){
38762 this.remove(panel);
38766 panel.tabItem = ti;
38771 updatePanelTitle : function(panel, title)
38773 if(this.activePanel == panel){
38774 this.updateTitle(title);
38777 var ti = this.tabs.getTab(panel.getEl().id);
38779 if(panel.tabTip !== undefined){
38780 ti.setTooltip(panel.tabTip);
38785 updateTitle : function(title){
38786 if(this.titleTextEl && !this.config.title){
38787 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38791 setActivePanel : function(panel)
38793 panel = this.getPanel(panel);
38794 if(this.activePanel && this.activePanel != panel){
38795 if(this.activePanel.setActiveState(false) === false){
38799 this.activePanel = panel;
38800 panel.setActiveState(true);
38801 if(this.panelSize){
38802 panel.setSize(this.panelSize.width, this.panelSize.height);
38805 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38807 this.updateTitle(panel.getTitle());
38809 this.fireEvent("invalidated", this);
38811 this.fireEvent("panelactivated", this, panel);
38815 * Shows the specified panel.
38816 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38817 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38819 showPanel : function(panel)
38821 panel = this.getPanel(panel);
38824 var tab = this.tabs.getTab(panel.getEl().id);
38825 if(tab.isHidden()){
38826 this.tabs.unhideTab(tab.id);
38830 this.setActivePanel(panel);
38837 * Get the active panel for this region.
38838 * @return {Roo.ContentPanel} The active panel or null
38840 getActivePanel : function(){
38841 return this.activePanel;
38844 validateVisibility : function(){
38845 if(this.panels.getCount() < 1){
38846 this.updateTitle(" ");
38847 this.closeBtn.hide();
38850 if(!this.isVisible()){
38857 * Adds the passed ContentPanel(s) to this region.
38858 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38859 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38861 add : function(panel)
38863 if(arguments.length > 1){
38864 for(var i = 0, len = arguments.length; i < len; i++) {
38865 this.add(arguments[i]);
38870 // if we have not been rendered yet, then we can not really do much of this..
38871 if (!this.bodyEl) {
38872 this.unrendered_panels.push(panel);
38879 if(this.hasPanel(panel)){
38880 this.showPanel(panel);
38883 panel.setRegion(this);
38884 this.panels.add(panel);
38885 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38886 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38887 // and hide them... ???
38888 this.bodyEl.dom.appendChild(panel.getEl().dom);
38889 if(panel.background !== true){
38890 this.setActivePanel(panel);
38892 this.fireEvent("paneladded", this, panel);
38899 this.initPanelAsTab(panel);
38903 if(panel.background !== true){
38904 this.tabs.activate(panel.getEl().id);
38906 this.fireEvent("paneladded", this, panel);
38911 * Hides the tab for the specified panel.
38912 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38914 hidePanel : function(panel){
38915 if(this.tabs && (panel = this.getPanel(panel))){
38916 this.tabs.hideTab(panel.getEl().id);
38921 * Unhides the tab for a previously hidden panel.
38922 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38924 unhidePanel : function(panel){
38925 if(this.tabs && (panel = this.getPanel(panel))){
38926 this.tabs.unhideTab(panel.getEl().id);
38930 clearPanels : function(){
38931 while(this.panels.getCount() > 0){
38932 this.remove(this.panels.first());
38937 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38938 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38939 * @param {Boolean} preservePanel Overrides the config preservePanel option
38940 * @return {Roo.ContentPanel} The panel that was removed
38942 remove : function(panel, preservePanel)
38944 panel = this.getPanel(panel);
38949 this.fireEvent("beforeremove", this, panel, e);
38950 if(e.cancel === true){
38953 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38954 var panelId = panel.getId();
38955 this.panels.removeKey(panelId);
38957 document.body.appendChild(panel.getEl().dom);
38960 this.tabs.removeTab(panel.getEl().id);
38961 }else if (!preservePanel){
38962 this.bodyEl.dom.removeChild(panel.getEl().dom);
38964 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38965 var p = this.panels.first();
38966 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38967 tempEl.appendChild(p.getEl().dom);
38968 this.bodyEl.update("");
38969 this.bodyEl.dom.appendChild(p.getEl().dom);
38971 this.updateTitle(p.getTitle());
38973 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38974 this.setActivePanel(p);
38976 panel.setRegion(null);
38977 if(this.activePanel == panel){
38978 this.activePanel = null;
38980 if(this.config.autoDestroy !== false && preservePanel !== true){
38981 try{panel.destroy();}catch(e){}
38983 this.fireEvent("panelremoved", this, panel);
38988 * Returns the TabPanel component used by this region
38989 * @return {Roo.TabPanel}
38991 getTabs : function(){
38995 createTool : function(parentEl, className){
38996 var btn = Roo.DomHelper.append(parentEl, {
38998 cls: "x-layout-tools-button",
39001 cls: "roo-layout-tools-button-inner " + className,
39005 btn.addClassOnOver("roo-layout-tools-button-over");
39010 * Ext JS Library 1.1.1
39011 * Copyright(c) 2006-2007, Ext JS, LLC.
39013 * Originally Released Under LGPL - original licence link has changed is not relivant.
39016 * <script type="text/javascript">
39022 * @class Roo.SplitLayoutRegion
39023 * @extends Roo.LayoutRegion
39024 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39026 Roo.bootstrap.layout.Split = function(config){
39027 this.cursor = config.cursor;
39028 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39031 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39033 splitTip : "Drag to resize.",
39034 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39035 useSplitTips : false,
39037 applyConfig : function(config){
39038 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39041 onRender : function(ctr,pos) {
39043 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39044 if(!this.config.split){
39049 var splitEl = Roo.DomHelper.append(ctr.dom, {
39051 id: this.el.id + "-split",
39052 cls: "roo-layout-split roo-layout-split-"+this.position,
39055 /** The SplitBar for this region
39056 * @type Roo.SplitBar */
39057 // does not exist yet...
39058 Roo.log([this.position, this.orientation]);
39060 this.split = new Roo.bootstrap.SplitBar({
39061 dragElement : splitEl,
39062 resizingElement: this.el,
39063 orientation : this.orientation
39066 this.split.on("moved", this.onSplitMove, this);
39067 this.split.useShim = this.config.useShim === true;
39068 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39069 if(this.useSplitTips){
39070 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39072 //if(config.collapsible){
39073 // this.split.el.on("dblclick", this.collapse, this);
39076 if(typeof this.config.minSize != "undefined"){
39077 this.split.minSize = this.config.minSize;
39079 if(typeof this.config.maxSize != "undefined"){
39080 this.split.maxSize = this.config.maxSize;
39082 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39083 this.hideSplitter();
39088 getHMaxSize : function(){
39089 var cmax = this.config.maxSize || 10000;
39090 var center = this.mgr.getRegion("center");
39091 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39094 getVMaxSize : function(){
39095 var cmax = this.config.maxSize || 10000;
39096 var center = this.mgr.getRegion("center");
39097 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39100 onSplitMove : function(split, newSize){
39101 this.fireEvent("resized", this, newSize);
39105 * Returns the {@link Roo.SplitBar} for this region.
39106 * @return {Roo.SplitBar}
39108 getSplitBar : function(){
39113 this.hideSplitter();
39114 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39117 hideSplitter : function(){
39119 this.split.el.setLocation(-2000,-2000);
39120 this.split.el.hide();
39126 this.split.el.show();
39128 Roo.bootstrap.layout.Split.superclass.show.call(this);
39131 beforeSlide: function(){
39132 if(Roo.isGecko){// firefox overflow auto bug workaround
39133 this.bodyEl.clip();
39135 this.tabs.bodyEl.clip();
39137 if(this.activePanel){
39138 this.activePanel.getEl().clip();
39140 if(this.activePanel.beforeSlide){
39141 this.activePanel.beforeSlide();
39147 afterSlide : function(){
39148 if(Roo.isGecko){// firefox overflow auto bug workaround
39149 this.bodyEl.unclip();
39151 this.tabs.bodyEl.unclip();
39153 if(this.activePanel){
39154 this.activePanel.getEl().unclip();
39155 if(this.activePanel.afterSlide){
39156 this.activePanel.afterSlide();
39162 initAutoHide : function(){
39163 if(this.autoHide !== false){
39164 if(!this.autoHideHd){
39165 var st = new Roo.util.DelayedTask(this.slideIn, this);
39166 this.autoHideHd = {
39167 "mouseout": function(e){
39168 if(!e.within(this.el, true)){
39172 "mouseover" : function(e){
39178 this.el.on(this.autoHideHd);
39182 clearAutoHide : function(){
39183 if(this.autoHide !== false){
39184 this.el.un("mouseout", this.autoHideHd.mouseout);
39185 this.el.un("mouseover", this.autoHideHd.mouseover);
39189 clearMonitor : function(){
39190 Roo.get(document).un("click", this.slideInIf, this);
39193 // these names are backwards but not changed for compat
39194 slideOut : function(){
39195 if(this.isSlid || this.el.hasActiveFx()){
39198 this.isSlid = true;
39199 if(this.collapseBtn){
39200 this.collapseBtn.hide();
39202 this.closeBtnState = this.closeBtn.getStyle('display');
39203 this.closeBtn.hide();
39205 this.stickBtn.show();
39208 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39209 this.beforeSlide();
39210 this.el.setStyle("z-index", 10001);
39211 this.el.slideIn(this.getSlideAnchor(), {
39212 callback: function(){
39214 this.initAutoHide();
39215 Roo.get(document).on("click", this.slideInIf, this);
39216 this.fireEvent("slideshow", this);
39223 afterSlideIn : function(){
39224 this.clearAutoHide();
39225 this.isSlid = false;
39226 this.clearMonitor();
39227 this.el.setStyle("z-index", "");
39228 if(this.collapseBtn){
39229 this.collapseBtn.show();
39231 this.closeBtn.setStyle('display', this.closeBtnState);
39233 this.stickBtn.hide();
39235 this.fireEvent("slidehide", this);
39238 slideIn : function(cb){
39239 if(!this.isSlid || this.el.hasActiveFx()){
39243 this.isSlid = false;
39244 this.beforeSlide();
39245 this.el.slideOut(this.getSlideAnchor(), {
39246 callback: function(){
39247 this.el.setLeftTop(-10000, -10000);
39249 this.afterSlideIn();
39257 slideInIf : function(e){
39258 if(!e.within(this.el)){
39263 animateCollapse : function(){
39264 this.beforeSlide();
39265 this.el.setStyle("z-index", 20000);
39266 var anchor = this.getSlideAnchor();
39267 this.el.slideOut(anchor, {
39268 callback : function(){
39269 this.el.setStyle("z-index", "");
39270 this.collapsedEl.slideIn(anchor, {duration:.3});
39272 this.el.setLocation(-10000,-10000);
39274 this.fireEvent("collapsed", this);
39281 animateExpand : function(){
39282 this.beforeSlide();
39283 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39284 this.el.setStyle("z-index", 20000);
39285 this.collapsedEl.hide({
39288 this.el.slideIn(this.getSlideAnchor(), {
39289 callback : function(){
39290 this.el.setStyle("z-index", "");
39293 this.split.el.show();
39295 this.fireEvent("invalidated", this);
39296 this.fireEvent("expanded", this);
39324 getAnchor : function(){
39325 return this.anchors[this.position];
39328 getCollapseAnchor : function(){
39329 return this.canchors[this.position];
39332 getSlideAnchor : function(){
39333 return this.sanchors[this.position];
39336 getAlignAdj : function(){
39337 var cm = this.cmargins;
39338 switch(this.position){
39354 getExpandAdj : function(){
39355 var c = this.collapsedEl, cm = this.cmargins;
39356 switch(this.position){
39358 return [-(cm.right+c.getWidth()+cm.left), 0];
39361 return [cm.right+c.getWidth()+cm.left, 0];
39364 return [0, -(cm.top+cm.bottom+c.getHeight())];
39367 return [0, cm.top+cm.bottom+c.getHeight()];
39373 * Ext JS Library 1.1.1
39374 * Copyright(c) 2006-2007, Ext JS, LLC.
39376 * Originally Released Under LGPL - original licence link has changed is not relivant.
39379 * <script type="text/javascript">
39382 * These classes are private internal classes
39384 Roo.bootstrap.layout.Center = function(config){
39385 config.region = "center";
39386 Roo.bootstrap.layout.Region.call(this, config);
39387 this.visible = true;
39388 this.minWidth = config.minWidth || 20;
39389 this.minHeight = config.minHeight || 20;
39392 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39394 // center panel can't be hidden
39398 // center panel can't be hidden
39401 getMinWidth: function(){
39402 return this.minWidth;
39405 getMinHeight: function(){
39406 return this.minHeight;
39420 Roo.bootstrap.layout.North = function(config)
39422 config.region = 'north';
39423 config.cursor = 'n-resize';
39425 Roo.bootstrap.layout.Split.call(this, config);
39429 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39430 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39431 this.split.el.addClass("roo-layout-split-v");
39433 //var size = config.initialSize || config.height;
39434 //if(this.el && typeof size != "undefined"){
39435 // this.el.setHeight(size);
39438 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39440 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39443 onRender : function(ctr, pos)
39445 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39446 var size = this.config.initialSize || this.config.height;
39447 if(this.el && typeof size != "undefined"){
39448 this.el.setHeight(size);
39453 getBox : function(){
39454 if(this.collapsed){
39455 return this.collapsedEl.getBox();
39457 var box = this.el.getBox();
39459 box.height += this.split.el.getHeight();
39464 updateBox : function(box){
39465 if(this.split && !this.collapsed){
39466 box.height -= this.split.el.getHeight();
39467 this.split.el.setLeft(box.x);
39468 this.split.el.setTop(box.y+box.height);
39469 this.split.el.setWidth(box.width);
39471 if(this.collapsed){
39472 this.updateBody(box.width, null);
39474 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39482 Roo.bootstrap.layout.South = function(config){
39483 config.region = 'south';
39484 config.cursor = 's-resize';
39485 Roo.bootstrap.layout.Split.call(this, config);
39487 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39488 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39489 this.split.el.addClass("roo-layout-split-v");
39494 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39495 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39497 onRender : function(ctr, pos)
39499 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39500 var size = this.config.initialSize || this.config.height;
39501 if(this.el && typeof size != "undefined"){
39502 this.el.setHeight(size);
39507 getBox : function(){
39508 if(this.collapsed){
39509 return this.collapsedEl.getBox();
39511 var box = this.el.getBox();
39513 var sh = this.split.el.getHeight();
39520 updateBox : function(box){
39521 if(this.split && !this.collapsed){
39522 var sh = this.split.el.getHeight();
39525 this.split.el.setLeft(box.x);
39526 this.split.el.setTop(box.y-sh);
39527 this.split.el.setWidth(box.width);
39529 if(this.collapsed){
39530 this.updateBody(box.width, null);
39532 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39536 Roo.bootstrap.layout.East = function(config){
39537 config.region = "east";
39538 config.cursor = "e-resize";
39539 Roo.bootstrap.layout.Split.call(this, config);
39541 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39542 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39543 this.split.el.addClass("roo-layout-split-h");
39547 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39548 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39550 onRender : function(ctr, pos)
39552 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39553 var size = this.config.initialSize || this.config.width;
39554 if(this.el && typeof size != "undefined"){
39555 this.el.setWidth(size);
39560 getBox : function(){
39561 if(this.collapsed){
39562 return this.collapsedEl.getBox();
39564 var box = this.el.getBox();
39566 var sw = this.split.el.getWidth();
39573 updateBox : function(box){
39574 if(this.split && !this.collapsed){
39575 var sw = this.split.el.getWidth();
39577 this.split.el.setLeft(box.x);
39578 this.split.el.setTop(box.y);
39579 this.split.el.setHeight(box.height);
39582 if(this.collapsed){
39583 this.updateBody(null, box.height);
39585 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39589 Roo.bootstrap.layout.West = function(config){
39590 config.region = "west";
39591 config.cursor = "w-resize";
39593 Roo.bootstrap.layout.Split.call(this, config);
39595 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39596 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39597 this.split.el.addClass("roo-layout-split-h");
39601 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39602 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39604 onRender: function(ctr, pos)
39606 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39607 var size = this.config.initialSize || this.config.width;
39608 if(typeof size != "undefined"){
39609 this.el.setWidth(size);
39613 getBox : function(){
39614 if(this.collapsed){
39615 return this.collapsedEl.getBox();
39617 var box = this.el.getBox();
39618 if (box.width == 0) {
39619 box.width = this.config.width; // kludge?
39622 box.width += this.split.el.getWidth();
39627 updateBox : function(box){
39628 if(this.split && !this.collapsed){
39629 var sw = this.split.el.getWidth();
39631 this.split.el.setLeft(box.x+box.width);
39632 this.split.el.setTop(box.y);
39633 this.split.el.setHeight(box.height);
39635 if(this.collapsed){
39636 this.updateBody(null, box.height);
39638 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39640 });Roo.namespace("Roo.bootstrap.panel");/*
39642 * Ext JS Library 1.1.1
39643 * Copyright(c) 2006-2007, Ext JS, LLC.
39645 * Originally Released Under LGPL - original licence link has changed is not relivant.
39648 * <script type="text/javascript">
39651 * @class Roo.ContentPanel
39652 * @extends Roo.util.Observable
39653 * A basic ContentPanel element.
39654 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39655 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39656 * @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
39657 * @cfg {Boolean} closable True if the panel can be closed/removed
39658 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39659 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39660 * @cfg {Toolbar} toolbar A toolbar for this panel
39661 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39662 * @cfg {String} title The title for this panel
39663 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39664 * @cfg {String} url Calls {@link #setUrl} with this value
39665 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39666 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39667 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39668 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39669 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39670 * @cfg {Boolean} badges render the badges
39671 * @cfg {String} cls extra classes to use
39672 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39675 * Create a new ContentPanel.
39676 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39677 * @param {String/Object} config A string to set only the title or a config object
39678 * @param {String} content (optional) Set the HTML content for this panel
39679 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39681 Roo.bootstrap.panel.Content = function( config){
39683 this.tpl = config.tpl || false;
39685 var el = config.el;
39686 var content = config.content;
39688 if(config.autoCreate){ // xtype is available if this is called from factory
39691 this.el = Roo.get(el);
39692 if(!this.el && config && config.autoCreate){
39693 if(typeof config.autoCreate == "object"){
39694 if(!config.autoCreate.id){
39695 config.autoCreate.id = config.id||el;
39697 this.el = Roo.DomHelper.append(document.body,
39698 config.autoCreate, true);
39702 cls: (config.cls || '') +
39703 (config.background ? ' bg-' + config.background : '') +
39704 " roo-layout-inactive-content",
39707 if (config.iframe) {
39711 style : 'border: 0px',
39712 src : 'about:blank'
39718 elcfg.html = config.html;
39722 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39723 if (config.iframe) {
39724 this.iframeEl = this.el.select('iframe',true).first();
39729 this.closable = false;
39730 this.loaded = false;
39731 this.active = false;
39734 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39736 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39738 this.wrapEl = this.el; //this.el.wrap();
39740 if (config.toolbar.items) {
39741 ti = config.toolbar.items ;
39742 delete config.toolbar.items ;
39746 this.toolbar.render(this.wrapEl, 'before');
39747 for(var i =0;i < ti.length;i++) {
39748 // Roo.log(['add child', items[i]]);
39749 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39751 this.toolbar.items = nitems;
39752 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39753 delete config.toolbar;
39757 // xtype created footer. - not sure if will work as we normally have to render first..
39758 if (this.footer && !this.footer.el && this.footer.xtype) {
39759 if (!this.wrapEl) {
39760 this.wrapEl = this.el.wrap();
39763 this.footer.container = this.wrapEl.createChild();
39765 this.footer = Roo.factory(this.footer, Roo);
39770 if(typeof config == "string"){
39771 this.title = config;
39773 Roo.apply(this, config);
39777 this.resizeEl = Roo.get(this.resizeEl, true);
39779 this.resizeEl = this.el;
39781 // handle view.xtype
39789 * Fires when this panel is activated.
39790 * @param {Roo.ContentPanel} this
39794 * @event deactivate
39795 * Fires when this panel is activated.
39796 * @param {Roo.ContentPanel} this
39798 "deactivate" : true,
39802 * Fires when this panel is resized if fitToFrame is true.
39803 * @param {Roo.ContentPanel} this
39804 * @param {Number} width The width after any component adjustments
39805 * @param {Number} height The height after any component adjustments
39811 * Fires when this tab is created
39812 * @param {Roo.ContentPanel} this
39823 if(this.autoScroll && !this.iframe){
39824 this.resizeEl.setStyle("overflow", "auto");
39826 // fix randome scrolling
39827 //this.el.on('scroll', function() {
39828 // Roo.log('fix random scolling');
39829 // this.scrollTo('top',0);
39832 content = content || this.content;
39834 this.setContent(content);
39836 if(config && config.url){
39837 this.setUrl(this.url, this.params, this.loadOnce);
39842 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39844 if (this.view && typeof(this.view.xtype) != 'undefined') {
39845 this.view.el = this.el.appendChild(document.createElement("div"));
39846 this.view = Roo.factory(this.view);
39847 this.view.render && this.view.render(false, '');
39851 this.fireEvent('render', this);
39854 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39864 setRegion : function(region){
39865 this.region = region;
39866 this.setActiveClass(region && !this.background);
39870 setActiveClass: function(state)
39873 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39874 this.el.setStyle('position','relative');
39876 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39877 this.el.setStyle('position', 'absolute');
39882 * Returns the toolbar for this Panel if one was configured.
39883 * @return {Roo.Toolbar}
39885 getToolbar : function(){
39886 return this.toolbar;
39889 setActiveState : function(active)
39891 this.active = active;
39892 this.setActiveClass(active);
39894 if(this.fireEvent("deactivate", this) === false){
39899 this.fireEvent("activate", this);
39903 * Updates this panel's element (not for iframe)
39904 * @param {String} content The new content
39905 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39907 setContent : function(content, loadScripts){
39912 this.el.update(content, loadScripts);
39915 ignoreResize : function(w, h){
39916 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39919 this.lastSize = {width: w, height: h};
39924 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39925 * @return {Roo.UpdateManager} The UpdateManager
39927 getUpdateManager : function(){
39931 return this.el.getUpdateManager();
39934 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39935 * Does not work with IFRAME contents
39936 * @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:
39939 url: "your-url.php",
39940 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39941 callback: yourFunction,
39942 scope: yourObject, //(optional scope)
39945 text: "Loading...",
39951 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39952 * 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.
39953 * @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}
39954 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39955 * @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.
39956 * @return {Roo.ContentPanel} this
39964 var um = this.el.getUpdateManager();
39965 um.update.apply(um, arguments);
39971 * 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.
39972 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39973 * @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)
39974 * @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)
39975 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39977 setUrl : function(url, params, loadOnce){
39979 this.iframeEl.dom.src = url;
39983 if(this.refreshDelegate){
39984 this.removeListener("activate", this.refreshDelegate);
39986 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39987 this.on("activate", this.refreshDelegate);
39988 return this.el.getUpdateManager();
39991 _handleRefresh : function(url, params, loadOnce){
39992 if(!loadOnce || !this.loaded){
39993 var updater = this.el.getUpdateManager();
39994 updater.update(url, params, this._setLoaded.createDelegate(this));
39998 _setLoaded : function(){
39999 this.loaded = true;
40003 * Returns this panel's id
40006 getId : function(){
40011 * Returns this panel's element - used by regiosn to add.
40012 * @return {Roo.Element}
40014 getEl : function(){
40015 return this.wrapEl || this.el;
40020 adjustForComponents : function(width, height)
40022 //Roo.log('adjustForComponents ');
40023 if(this.resizeEl != this.el){
40024 width -= this.el.getFrameWidth('lr');
40025 height -= this.el.getFrameWidth('tb');
40028 var te = this.toolbar.getEl();
40029 te.setWidth(width);
40030 height -= te.getHeight();
40033 var te = this.footer.getEl();
40034 te.setWidth(width);
40035 height -= te.getHeight();
40039 if(this.adjustments){
40040 width += this.adjustments[0];
40041 height += this.adjustments[1];
40043 return {"width": width, "height": height};
40046 setSize : function(width, height){
40047 if(this.fitToFrame && !this.ignoreResize(width, height)){
40048 if(this.fitContainer && this.resizeEl != this.el){
40049 this.el.setSize(width, height);
40051 var size = this.adjustForComponents(width, height);
40053 this.iframeEl.setSize(width,height);
40056 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40057 this.fireEvent('resize', this, size.width, size.height);
40064 * Returns this panel's title
40067 getTitle : function(){
40069 if (typeof(this.title) != 'object') {
40074 for (var k in this.title) {
40075 if (!this.title.hasOwnProperty(k)) {
40079 if (k.indexOf('-') >= 0) {
40080 var s = k.split('-');
40081 for (var i = 0; i<s.length; i++) {
40082 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40085 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40092 * Set this panel's title
40093 * @param {String} title
40095 setTitle : function(title){
40096 this.title = title;
40098 this.region.updatePanelTitle(this, title);
40103 * Returns true is this panel was configured to be closable
40104 * @return {Boolean}
40106 isClosable : function(){
40107 return this.closable;
40110 beforeSlide : function(){
40112 this.resizeEl.clip();
40115 afterSlide : function(){
40117 this.resizeEl.unclip();
40121 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40122 * Will fail silently if the {@link #setUrl} method has not been called.
40123 * This does not activate the panel, just updates its content.
40125 refresh : function(){
40126 if(this.refreshDelegate){
40127 this.loaded = false;
40128 this.refreshDelegate();
40133 * Destroys this panel
40135 destroy : function(){
40136 this.el.removeAllListeners();
40137 var tempEl = document.createElement("span");
40138 tempEl.appendChild(this.el.dom);
40139 tempEl.innerHTML = "";
40145 * form - if the content panel contains a form - this is a reference to it.
40146 * @type {Roo.form.Form}
40150 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40151 * This contains a reference to it.
40157 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40167 * @param {Object} cfg Xtype definition of item to add.
40171 getChildContainer: function () {
40172 return this.getEl();
40177 var ret = new Roo.factory(cfg);
40182 if (cfg.xtype.match(/^Form$/)) {
40185 //if (this.footer) {
40186 // el = this.footer.container.insertSibling(false, 'before');
40188 el = this.el.createChild();
40191 this.form = new Roo.form.Form(cfg);
40194 if ( this.form.allItems.length) {
40195 this.form.render(el.dom);
40199 // should only have one of theses..
40200 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40201 // views.. should not be just added - used named prop 'view''
40203 cfg.el = this.el.appendChild(document.createElement("div"));
40206 var ret = new Roo.factory(cfg);
40208 ret.render && ret.render(false, ''); // render blank..
40218 * @class Roo.bootstrap.panel.Grid
40219 * @extends Roo.bootstrap.panel.Content
40221 * Create a new GridPanel.
40222 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40223 * @param {Object} config A the config object
40229 Roo.bootstrap.panel.Grid = function(config)
40233 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40234 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40236 config.el = this.wrapper;
40237 //this.el = this.wrapper;
40239 if (config.container) {
40240 // ctor'ed from a Border/panel.grid
40243 this.wrapper.setStyle("overflow", "hidden");
40244 this.wrapper.addClass('roo-grid-container');
40249 if(config.toolbar){
40250 var tool_el = this.wrapper.createChild();
40251 this.toolbar = Roo.factory(config.toolbar);
40253 if (config.toolbar.items) {
40254 ti = config.toolbar.items ;
40255 delete config.toolbar.items ;
40259 this.toolbar.render(tool_el);
40260 for(var i =0;i < ti.length;i++) {
40261 // Roo.log(['add child', items[i]]);
40262 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40264 this.toolbar.items = nitems;
40266 delete config.toolbar;
40269 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40270 config.grid.scrollBody = true;;
40271 config.grid.monitorWindowResize = false; // turn off autosizing
40272 config.grid.autoHeight = false;
40273 config.grid.autoWidth = false;
40275 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40277 if (config.background) {
40278 // render grid on panel activation (if panel background)
40279 this.on('activate', function(gp) {
40280 if (!gp.grid.rendered) {
40281 gp.grid.render(this.wrapper);
40282 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40287 this.grid.render(this.wrapper);
40288 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40291 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40292 // ??? needed ??? config.el = this.wrapper;
40297 // xtype created footer. - not sure if will work as we normally have to render first..
40298 if (this.footer && !this.footer.el && this.footer.xtype) {
40300 var ctr = this.grid.getView().getFooterPanel(true);
40301 this.footer.dataSource = this.grid.dataSource;
40302 this.footer = Roo.factory(this.footer, Roo);
40303 this.footer.render(ctr);
40313 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40314 getId : function(){
40315 return this.grid.id;
40319 * Returns the grid for this panel
40320 * @return {Roo.bootstrap.Table}
40322 getGrid : function(){
40326 setSize : function(width, height){
40327 if(!this.ignoreResize(width, height)){
40328 var grid = this.grid;
40329 var size = this.adjustForComponents(width, height);
40330 // tfoot is not a footer?
40333 var gridel = grid.getGridEl();
40334 gridel.setSize(size.width, size.height);
40336 var tbd = grid.getGridEl().select('tbody', true).first();
40337 var thd = grid.getGridEl().select('thead',true).first();
40338 var tbf= grid.getGridEl().select('tfoot', true).first();
40341 size.height -= tbf.getHeight();
40344 size.height -= thd.getHeight();
40347 tbd.setSize(size.width, size.height );
40348 // this is for the account management tab -seems to work there.
40349 var thd = grid.getGridEl().select('thead',true).first();
40351 // tbd.setSize(size.width, size.height - thd.getHeight());
40360 beforeSlide : function(){
40361 this.grid.getView().scroller.clip();
40364 afterSlide : function(){
40365 this.grid.getView().scroller.unclip();
40368 destroy : function(){
40369 this.grid.destroy();
40371 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40376 * @class Roo.bootstrap.panel.Nest
40377 * @extends Roo.bootstrap.panel.Content
40379 * Create a new Panel, that can contain a layout.Border.
40382 * @param {Roo.BorderLayout} layout The layout for this panel
40383 * @param {String/Object} config A string to set only the title or a config object
40385 Roo.bootstrap.panel.Nest = function(config)
40387 // construct with only one argument..
40388 /* FIXME - implement nicer consturctors
40389 if (layout.layout) {
40391 layout = config.layout;
40392 delete config.layout;
40394 if (layout.xtype && !layout.getEl) {
40395 // then layout needs constructing..
40396 layout = Roo.factory(layout, Roo);
40400 config.el = config.layout.getEl();
40402 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40404 config.layout.monitorWindowResize = false; // turn off autosizing
40405 this.layout = config.layout;
40406 this.layout.getEl().addClass("roo-layout-nested-layout");
40407 this.layout.parent = this;
40414 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40416 setSize : function(width, height){
40417 if(!this.ignoreResize(width, height)){
40418 var size = this.adjustForComponents(width, height);
40419 var el = this.layout.getEl();
40420 if (size.height < 1) {
40421 el.setWidth(size.width);
40423 el.setSize(size.width, size.height);
40425 var touch = el.dom.offsetWidth;
40426 this.layout.layout();
40427 // ie requires a double layout on the first pass
40428 if(Roo.isIE && !this.initialized){
40429 this.initialized = true;
40430 this.layout.layout();
40435 // activate all subpanels if not currently active..
40437 setActiveState : function(active){
40438 this.active = active;
40439 this.setActiveClass(active);
40442 this.fireEvent("deactivate", this);
40446 this.fireEvent("activate", this);
40447 // not sure if this should happen before or after..
40448 if (!this.layout) {
40449 return; // should not happen..
40452 for (var r in this.layout.regions) {
40453 reg = this.layout.getRegion(r);
40454 if (reg.getActivePanel()) {
40455 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40456 reg.setActivePanel(reg.getActivePanel());
40459 if (!reg.panels.length) {
40462 reg.showPanel(reg.getPanel(0));
40471 * Returns the nested BorderLayout for this panel
40472 * @return {Roo.BorderLayout}
40474 getLayout : function(){
40475 return this.layout;
40479 * Adds a xtype elements to the layout of the nested panel
40483 xtype : 'ContentPanel',
40490 xtype : 'NestedLayoutPanel',
40496 items : [ ... list of content panels or nested layout panels.. ]
40500 * @param {Object} cfg Xtype definition of item to add.
40502 addxtype : function(cfg) {
40503 return this.layout.addxtype(cfg);
40508 * Ext JS Library 1.1.1
40509 * Copyright(c) 2006-2007, Ext JS, LLC.
40511 * Originally Released Under LGPL - original licence link has changed is not relivant.
40514 * <script type="text/javascript">
40517 * @class Roo.TabPanel
40518 * @extends Roo.util.Observable
40519 * A lightweight tab container.
40523 // basic tabs 1, built from existing content
40524 var tabs = new Roo.TabPanel("tabs1");
40525 tabs.addTab("script", "View Script");
40526 tabs.addTab("markup", "View Markup");
40527 tabs.activate("script");
40529 // more advanced tabs, built from javascript
40530 var jtabs = new Roo.TabPanel("jtabs");
40531 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40533 // set up the UpdateManager
40534 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40535 var updater = tab2.getUpdateManager();
40536 updater.setDefaultUrl("ajax1.htm");
40537 tab2.on('activate', updater.refresh, updater, true);
40539 // Use setUrl for Ajax loading
40540 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40541 tab3.setUrl("ajax2.htm", null, true);
40544 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40547 jtabs.activate("jtabs-1");
40550 * Create a new TabPanel.
40551 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40552 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40554 Roo.bootstrap.panel.Tabs = function(config){
40556 * The container element for this TabPanel.
40557 * @type Roo.Element
40559 this.el = Roo.get(config.el);
40562 if(typeof config == "boolean"){
40563 this.tabPosition = config ? "bottom" : "top";
40565 Roo.apply(this, config);
40569 if(this.tabPosition == "bottom"){
40570 // if tabs are at the bottom = create the body first.
40571 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40572 this.el.addClass("roo-tabs-bottom");
40574 // next create the tabs holders
40576 if (this.tabPosition == "west"){
40578 var reg = this.region; // fake it..
40580 if (!reg.mgr.parent) {
40583 reg = reg.mgr.parent.region;
40585 Roo.log("got nest?");
40587 if (reg.mgr.getRegion('west')) {
40588 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40589 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40590 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40591 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40592 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40600 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40601 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40602 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40603 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40608 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40611 // finally - if tabs are at the top, then create the body last..
40612 if(this.tabPosition != "bottom"){
40613 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40614 * @type Roo.Element
40616 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40617 this.el.addClass("roo-tabs-top");
40621 this.bodyEl.setStyle("position", "relative");
40623 this.active = null;
40624 this.activateDelegate = this.activate.createDelegate(this);
40629 * Fires when the active tab changes
40630 * @param {Roo.TabPanel} this
40631 * @param {Roo.TabPanelItem} activePanel The new active tab
40635 * @event beforetabchange
40636 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40637 * @param {Roo.TabPanel} this
40638 * @param {Object} e Set cancel to true on this object to cancel the tab change
40639 * @param {Roo.TabPanelItem} tab The tab being changed to
40641 "beforetabchange" : true
40644 Roo.EventManager.onWindowResize(this.onResize, this);
40645 this.cpad = this.el.getPadding("lr");
40646 this.hiddenCount = 0;
40649 // toolbar on the tabbar support...
40650 if (this.toolbar) {
40651 alert("no toolbar support yet");
40652 this.toolbar = false;
40654 var tcfg = this.toolbar;
40655 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40656 this.toolbar = new Roo.Toolbar(tcfg);
40657 if (Roo.isSafari) {
40658 var tbl = tcfg.container.child('table', true);
40659 tbl.setAttribute('width', '100%');
40667 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40670 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40672 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40674 tabPosition : "top",
40676 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40678 currentTabWidth : 0,
40680 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40684 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40688 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40690 preferredTabWidth : 175,
40692 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40694 resizeTabs : false,
40696 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40698 monitorResize : true,
40700 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40702 toolbar : false, // set by caller..
40704 region : false, /// set by caller
40706 disableTooltips : true, // not used yet...
40709 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40710 * @param {String} id The id of the div to use <b>or create</b>
40711 * @param {String} text The text for the tab
40712 * @param {String} content (optional) Content to put in the TabPanelItem body
40713 * @param {Boolean} closable (optional) True to create a close icon on the tab
40714 * @return {Roo.TabPanelItem} The created TabPanelItem
40716 addTab : function(id, text, content, closable, tpl)
40718 var item = new Roo.bootstrap.panel.TabItem({
40722 closable : closable,
40725 this.addTabItem(item);
40727 item.setContent(content);
40733 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40734 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40735 * @return {Roo.TabPanelItem}
40737 getTab : function(id){
40738 return this.items[id];
40742 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40743 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40745 hideTab : function(id){
40746 var t = this.items[id];
40749 this.hiddenCount++;
40750 this.autoSizeTabs();
40755 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40756 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40758 unhideTab : function(id){
40759 var t = this.items[id];
40761 t.setHidden(false);
40762 this.hiddenCount--;
40763 this.autoSizeTabs();
40768 * Adds an existing {@link Roo.TabPanelItem}.
40769 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40771 addTabItem : function(item)
40773 this.items[item.id] = item;
40774 this.items.push(item);
40775 this.autoSizeTabs();
40776 // if(this.resizeTabs){
40777 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40778 // this.autoSizeTabs();
40780 // item.autoSize();
40785 * Removes a {@link Roo.TabPanelItem}.
40786 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40788 removeTab : function(id){
40789 var items = this.items;
40790 var tab = items[id];
40791 if(!tab) { return; }
40792 var index = items.indexOf(tab);
40793 if(this.active == tab && items.length > 1){
40794 var newTab = this.getNextAvailable(index);
40799 this.stripEl.dom.removeChild(tab.pnode.dom);
40800 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40801 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40803 items.splice(index, 1);
40804 delete this.items[tab.id];
40805 tab.fireEvent("close", tab);
40806 tab.purgeListeners();
40807 this.autoSizeTabs();
40810 getNextAvailable : function(start){
40811 var items = this.items;
40813 // look for a next tab that will slide over to
40814 // replace the one being removed
40815 while(index < items.length){
40816 var item = items[++index];
40817 if(item && !item.isHidden()){
40821 // if one isn't found select the previous tab (on the left)
40824 var item = items[--index];
40825 if(item && !item.isHidden()){
40833 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40834 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40836 disableTab : function(id){
40837 var tab = this.items[id];
40838 if(tab && this.active != tab){
40844 * Enables a {@link Roo.TabPanelItem} that is disabled.
40845 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40847 enableTab : function(id){
40848 var tab = this.items[id];
40853 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40854 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40855 * @return {Roo.TabPanelItem} The TabPanelItem.
40857 activate : function(id)
40859 //Roo.log('activite:' + id);
40861 var tab = this.items[id];
40865 if(tab == this.active || tab.disabled){
40869 this.fireEvent("beforetabchange", this, e, tab);
40870 if(e.cancel !== true && !tab.disabled){
40872 this.active.hide();
40874 this.active = this.items[id];
40875 this.active.show();
40876 this.fireEvent("tabchange", this, this.active);
40882 * Gets the active {@link Roo.TabPanelItem}.
40883 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40885 getActiveTab : function(){
40886 return this.active;
40890 * Updates the tab body element to fit the height of the container element
40891 * for overflow scrolling
40892 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40894 syncHeight : function(targetHeight){
40895 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40896 var bm = this.bodyEl.getMargins();
40897 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40898 this.bodyEl.setHeight(newHeight);
40902 onResize : function(){
40903 if(this.monitorResize){
40904 this.autoSizeTabs();
40909 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40911 beginUpdate : function(){
40912 this.updating = true;
40916 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40918 endUpdate : function(){
40919 this.updating = false;
40920 this.autoSizeTabs();
40924 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40926 autoSizeTabs : function()
40928 var count = this.items.length;
40929 var vcount = count - this.hiddenCount;
40932 this.stripEl.hide();
40934 this.stripEl.show();
40937 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40942 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40943 var availWidth = Math.floor(w / vcount);
40944 var b = this.stripBody;
40945 if(b.getWidth() > w){
40946 var tabs = this.items;
40947 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40948 if(availWidth < this.minTabWidth){
40949 /*if(!this.sleft){ // incomplete scrolling code
40950 this.createScrollButtons();
40953 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40956 if(this.currentTabWidth < this.preferredTabWidth){
40957 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40963 * Returns the number of tabs in this TabPanel.
40966 getCount : function(){
40967 return this.items.length;
40971 * Resizes all the tabs to the passed width
40972 * @param {Number} The new width
40974 setTabWidth : function(width){
40975 this.currentTabWidth = width;
40976 for(var i = 0, len = this.items.length; i < len; i++) {
40977 if(!this.items[i].isHidden()) {
40978 this.items[i].setWidth(width);
40984 * Destroys this TabPanel
40985 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40987 destroy : function(removeEl){
40988 Roo.EventManager.removeResizeListener(this.onResize, this);
40989 for(var i = 0, len = this.items.length; i < len; i++){
40990 this.items[i].purgeListeners();
40992 if(removeEl === true){
40993 this.el.update("");
40998 createStrip : function(container)
41000 var strip = document.createElement("nav");
41001 strip.className = Roo.bootstrap.version == 4 ?
41002 "navbar-light bg-light" :
41003 "navbar navbar-default"; //"x-tabs-wrap";
41004 container.appendChild(strip);
41008 createStripList : function(strip)
41010 // div wrapper for retard IE
41011 // returns the "tr" element.
41012 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41013 //'<div class="x-tabs-strip-wrap">'+
41014 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41015 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41016 return strip.firstChild; //.firstChild.firstChild.firstChild;
41018 createBody : function(container)
41020 var body = document.createElement("div");
41021 Roo.id(body, "tab-body");
41022 //Roo.fly(body).addClass("x-tabs-body");
41023 Roo.fly(body).addClass("tab-content");
41024 container.appendChild(body);
41027 createItemBody :function(bodyEl, id){
41028 var body = Roo.getDom(id);
41030 body = document.createElement("div");
41033 //Roo.fly(body).addClass("x-tabs-item-body");
41034 Roo.fly(body).addClass("tab-pane");
41035 bodyEl.insertBefore(body, bodyEl.firstChild);
41039 createStripElements : function(stripEl, text, closable, tpl)
41041 var td = document.createElement("li"); // was td..
41042 td.className = 'nav-item';
41044 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41047 stripEl.appendChild(td);
41049 td.className = "x-tabs-closable";
41050 if(!this.closeTpl){
41051 this.closeTpl = new Roo.Template(
41052 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41053 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41054 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41057 var el = this.closeTpl.overwrite(td, {"text": text});
41058 var close = el.getElementsByTagName("div")[0];
41059 var inner = el.getElementsByTagName("em")[0];
41060 return {"el": el, "close": close, "inner": inner};
41063 // not sure what this is..
41064 // if(!this.tabTpl){
41065 //this.tabTpl = new Roo.Template(
41066 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41067 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41069 // this.tabTpl = new Roo.Template(
41070 // '<a href="#">' +
41071 // '<span unselectable="on"' +
41072 // (this.disableTooltips ? '' : ' title="{text}"') +
41073 // ' >{text}</span></a>'
41079 var template = tpl || this.tabTpl || false;
41082 template = new Roo.Template(
41083 Roo.bootstrap.version == 4 ?
41085 '<a class="nav-link" href="#" unselectable="on"' +
41086 (this.disableTooltips ? '' : ' title="{text}"') +
41089 '<a class="nav-link" href="#">' +
41090 '<span unselectable="on"' +
41091 (this.disableTooltips ? '' : ' title="{text}"') +
41092 ' >{text}</span></a>'
41097 switch (typeof(template)) {
41101 template = new Roo.Template(template);
41107 var el = template.overwrite(td, {"text": text});
41109 var inner = el.getElementsByTagName("span")[0];
41111 return {"el": el, "inner": inner};
41119 * @class Roo.TabPanelItem
41120 * @extends Roo.util.Observable
41121 * Represents an individual item (tab plus body) in a TabPanel.
41122 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41123 * @param {String} id The id of this TabPanelItem
41124 * @param {String} text The text for the tab of this TabPanelItem
41125 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41127 Roo.bootstrap.panel.TabItem = function(config){
41129 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41130 * @type Roo.TabPanel
41132 this.tabPanel = config.panel;
41134 * The id for this TabPanelItem
41137 this.id = config.id;
41139 this.disabled = false;
41141 this.text = config.text;
41143 this.loaded = false;
41144 this.closable = config.closable;
41147 * The body element for this TabPanelItem.
41148 * @type Roo.Element
41150 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41151 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41152 this.bodyEl.setStyle("display", "block");
41153 this.bodyEl.setStyle("zoom", "1");
41154 //this.hideAction();
41156 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41158 this.el = Roo.get(els.el);
41159 this.inner = Roo.get(els.inner, true);
41160 this.textEl = Roo.bootstrap.version == 4 ?
41161 this.el : Roo.get(this.el.dom.firstChild, true);
41163 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41164 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41167 // this.el.on("mousedown", this.onTabMouseDown, this);
41168 this.el.on("click", this.onTabClick, this);
41170 if(config.closable){
41171 var c = Roo.get(els.close, true);
41172 c.dom.title = this.closeText;
41173 c.addClassOnOver("close-over");
41174 c.on("click", this.closeClick, this);
41180 * Fires when this tab becomes the active tab.
41181 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41182 * @param {Roo.TabPanelItem} this
41186 * @event beforeclose
41187 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41188 * @param {Roo.TabPanelItem} this
41189 * @param {Object} e Set cancel to true on this object to cancel the close.
41191 "beforeclose": true,
41194 * Fires when this tab is closed.
41195 * @param {Roo.TabPanelItem} this
41199 * @event deactivate
41200 * Fires when this tab is no longer the active tab.
41201 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41202 * @param {Roo.TabPanelItem} this
41204 "deactivate" : true
41206 this.hidden = false;
41208 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41211 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41213 purgeListeners : function(){
41214 Roo.util.Observable.prototype.purgeListeners.call(this);
41215 this.el.removeAllListeners();
41218 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41221 this.status_node.addClass("active");
41224 this.tabPanel.stripWrap.repaint();
41226 this.fireEvent("activate", this.tabPanel, this);
41230 * Returns true if this tab is the active tab.
41231 * @return {Boolean}
41233 isActive : function(){
41234 return this.tabPanel.getActiveTab() == this;
41238 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41241 this.status_node.removeClass("active");
41243 this.fireEvent("deactivate", this.tabPanel, this);
41246 hideAction : function(){
41247 this.bodyEl.hide();
41248 this.bodyEl.setStyle("position", "absolute");
41249 this.bodyEl.setLeft("-20000px");
41250 this.bodyEl.setTop("-20000px");
41253 showAction : function(){
41254 this.bodyEl.setStyle("position", "relative");
41255 this.bodyEl.setTop("");
41256 this.bodyEl.setLeft("");
41257 this.bodyEl.show();
41261 * Set the tooltip for the tab.
41262 * @param {String} tooltip The tab's tooltip
41264 setTooltip : function(text){
41265 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41266 this.textEl.dom.qtip = text;
41267 this.textEl.dom.removeAttribute('title');
41269 this.textEl.dom.title = text;
41273 onTabClick : function(e){
41274 e.preventDefault();
41275 this.tabPanel.activate(this.id);
41278 onTabMouseDown : function(e){
41279 e.preventDefault();
41280 this.tabPanel.activate(this.id);
41283 getWidth : function(){
41284 return this.inner.getWidth();
41287 setWidth : function(width){
41288 var iwidth = width - this.linode.getPadding("lr");
41289 this.inner.setWidth(iwidth);
41290 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41291 this.linode.setWidth(width);
41295 * Show or hide the tab
41296 * @param {Boolean} hidden True to hide or false to show.
41298 setHidden : function(hidden){
41299 this.hidden = hidden;
41300 this.linode.setStyle("display", hidden ? "none" : "");
41304 * Returns true if this tab is "hidden"
41305 * @return {Boolean}
41307 isHidden : function(){
41308 return this.hidden;
41312 * Returns the text for this tab
41315 getText : function(){
41319 autoSize : function(){
41320 //this.el.beginMeasure();
41321 this.textEl.setWidth(1);
41323 * #2804 [new] Tabs in Roojs
41324 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41326 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41327 //this.el.endMeasure();
41331 * Sets the text for the tab (Note: this also sets the tooltip text)
41332 * @param {String} text The tab's text and tooltip
41334 setText : function(text){
41336 this.textEl.update(text);
41337 this.setTooltip(text);
41338 //if(!this.tabPanel.resizeTabs){
41339 // this.autoSize();
41343 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41345 activate : function(){
41346 this.tabPanel.activate(this.id);
41350 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41352 disable : function(){
41353 if(this.tabPanel.active != this){
41354 this.disabled = true;
41355 this.status_node.addClass("disabled");
41360 * Enables this TabPanelItem if it was previously disabled.
41362 enable : function(){
41363 this.disabled = false;
41364 this.status_node.removeClass("disabled");
41368 * Sets the content for this TabPanelItem.
41369 * @param {String} content The content
41370 * @param {Boolean} loadScripts true to look for and load scripts
41372 setContent : function(content, loadScripts){
41373 this.bodyEl.update(content, loadScripts);
41377 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41378 * @return {Roo.UpdateManager} The UpdateManager
41380 getUpdateManager : function(){
41381 return this.bodyEl.getUpdateManager();
41385 * Set a URL to be used to load the content for this TabPanelItem.
41386 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41387 * @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)
41388 * @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)
41389 * @return {Roo.UpdateManager} The UpdateManager
41391 setUrl : function(url, params, loadOnce){
41392 if(this.refreshDelegate){
41393 this.un('activate', this.refreshDelegate);
41395 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41396 this.on("activate", this.refreshDelegate);
41397 return this.bodyEl.getUpdateManager();
41401 _handleRefresh : function(url, params, loadOnce){
41402 if(!loadOnce || !this.loaded){
41403 var updater = this.bodyEl.getUpdateManager();
41404 updater.update(url, params, this._setLoaded.createDelegate(this));
41409 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41410 * Will fail silently if the setUrl method has not been called.
41411 * This does not activate the panel, just updates its content.
41413 refresh : function(){
41414 if(this.refreshDelegate){
41415 this.loaded = false;
41416 this.refreshDelegate();
41421 _setLoaded : function(){
41422 this.loaded = true;
41426 closeClick : function(e){
41429 this.fireEvent("beforeclose", this, o);
41430 if(o.cancel !== true){
41431 this.tabPanel.removeTab(this.id);
41435 * The text displayed in the tooltip for the close icon.
41438 closeText : "Close this tab"
41441 * This script refer to:
41442 * Title: International Telephone Input
41443 * Author: Jack O'Connor
41444 * Code version: v12.1.12
41445 * Availability: https://github.com/jackocnr/intl-tel-input.git
41448 Roo.bootstrap.PhoneInputData = function() {
41451 "Afghanistan (افغانستان)",
41456 "Albania (Shqipëri)",
41461 "Algeria (الجزائر)",
41486 "Antigua and Barbuda",
41496 "Armenia (Հայաստան)",
41512 "Austria (Österreich)",
41517 "Azerbaijan (Azərbaycan)",
41527 "Bahrain (البحرين)",
41532 "Bangladesh (বাংলাদেশ)",
41542 "Belarus (Беларусь)",
41547 "Belgium (België)",
41577 "Bosnia and Herzegovina (Босна и Херцеговина)",
41592 "British Indian Ocean Territory",
41597 "British Virgin Islands",
41607 "Bulgaria (България)",
41617 "Burundi (Uburundi)",
41622 "Cambodia (កម្ពុជា)",
41627 "Cameroon (Cameroun)",
41636 ["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"]
41639 "Cape Verde (Kabu Verdi)",
41644 "Caribbean Netherlands",
41655 "Central African Republic (République centrafricaine)",
41675 "Christmas Island",
41681 "Cocos (Keeling) Islands",
41692 "Comoros (جزر القمر)",
41697 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41702 "Congo (Republic) (Congo-Brazzaville)",
41722 "Croatia (Hrvatska)",
41743 "Czech Republic (Česká republika)",
41748 "Denmark (Danmark)",
41763 "Dominican Republic (República Dominicana)",
41767 ["809", "829", "849"]
41785 "Equatorial Guinea (Guinea Ecuatorial)",
41805 "Falkland Islands (Islas Malvinas)",
41810 "Faroe Islands (Føroyar)",
41831 "French Guiana (Guyane française)",
41836 "French Polynesia (Polynésie française)",
41851 "Georgia (საქართველო)",
41856 "Germany (Deutschland)",
41876 "Greenland (Kalaallit Nunaat)",
41913 "Guinea-Bissau (Guiné Bissau)",
41938 "Hungary (Magyarország)",
41943 "Iceland (Ísland)",
41963 "Iraq (العراق)",
41979 "Israel (ישראל)",
42006 "Jordan (الأردن)",
42011 "Kazakhstan (Казахстан)",
42032 "Kuwait (الكويت)",
42037 "Kyrgyzstan (Кыргызстан)",
42047 "Latvia (Latvija)",
42052 "Lebanon (لبنان)",
42067 "Libya (ليبيا)",
42077 "Lithuania (Lietuva)",
42092 "Macedonia (FYROM) (Македонија)",
42097 "Madagascar (Madagasikara)",
42127 "Marshall Islands",
42137 "Mauritania (موريتانيا)",
42142 "Mauritius (Moris)",
42163 "Moldova (Republica Moldova)",
42173 "Mongolia (Монгол)",
42178 "Montenegro (Crna Gora)",
42188 "Morocco (المغرب)",
42194 "Mozambique (Moçambique)",
42199 "Myanmar (Burma) (မြန်မာ)",
42204 "Namibia (Namibië)",
42219 "Netherlands (Nederland)",
42224 "New Caledonia (Nouvelle-Calédonie)",
42259 "North Korea (조선 민주주의 인민 공화국)",
42264 "Northern Mariana Islands",
42280 "Pakistan (پاکستان)",
42290 "Palestine (فلسطين)",
42300 "Papua New Guinea",
42342 "Réunion (La Réunion)",
42348 "Romania (România)",
42364 "Saint Barthélemy",
42375 "Saint Kitts and Nevis",
42385 "Saint Martin (Saint-Martin (partie française))",
42391 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42396 "Saint Vincent and the Grenadines",
42411 "São Tomé and Príncipe (São Tomé e Príncipe)",
42416 "Saudi Arabia (المملكة العربية السعودية)",
42421 "Senegal (Sénégal)",
42451 "Slovakia (Slovensko)",
42456 "Slovenia (Slovenija)",
42466 "Somalia (Soomaaliya)",
42476 "South Korea (대한민국)",
42481 "South Sudan (جنوب السودان)",
42491 "Sri Lanka (ශ්රී ලංකාව)",
42496 "Sudan (السودان)",
42506 "Svalbard and Jan Mayen",
42517 "Sweden (Sverige)",
42522 "Switzerland (Schweiz)",
42527 "Syria (سوريا)",
42572 "Trinidad and Tobago",
42577 "Tunisia (تونس)",
42582 "Turkey (Türkiye)",
42592 "Turks and Caicos Islands",
42602 "U.S. Virgin Islands",
42612 "Ukraine (Україна)",
42617 "United Arab Emirates (الإمارات العربية المتحدة)",
42639 "Uzbekistan (Oʻzbekiston)",
42649 "Vatican City (Città del Vaticano)",
42660 "Vietnam (Việt Nam)",
42665 "Wallis and Futuna (Wallis-et-Futuna)",
42670 "Western Sahara (الصحراء الغربية)",
42676 "Yemen (اليمن)",
42700 * This script refer to:
42701 * Title: International Telephone Input
42702 * Author: Jack O'Connor
42703 * Code version: v12.1.12
42704 * Availability: https://github.com/jackocnr/intl-tel-input.git
42708 * @class Roo.bootstrap.PhoneInput
42709 * @extends Roo.bootstrap.TriggerField
42710 * An input with International dial-code selection
42712 * @cfg {String} defaultDialCode default '+852'
42713 * @cfg {Array} preferedCountries default []
42716 * Create a new PhoneInput.
42717 * @param {Object} config Configuration options
42720 Roo.bootstrap.PhoneInput = function(config) {
42721 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42724 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42726 listWidth: undefined,
42728 selectedClass: 'active',
42730 invalidClass : "has-warning",
42732 validClass: 'has-success',
42734 allowed: '0123456789',
42739 * @cfg {String} defaultDialCode The default dial code when initializing the input
42741 defaultDialCode: '+852',
42744 * @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
42746 preferedCountries: false,
42748 getAutoCreate : function()
42750 var data = Roo.bootstrap.PhoneInputData();
42751 var align = this.labelAlign || this.parentLabelAlign();
42754 this.allCountries = [];
42755 this.dialCodeMapping = [];
42757 for (var i = 0; i < data.length; i++) {
42759 this.allCountries[i] = {
42763 priority: c[3] || 0,
42764 areaCodes: c[4] || null
42766 this.dialCodeMapping[c[2]] = {
42769 priority: c[3] || 0,
42770 areaCodes: c[4] || null
42782 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42783 maxlength: this.max_length,
42784 cls : 'form-control tel-input',
42785 autocomplete: 'new-password'
42788 var hiddenInput = {
42791 cls: 'hidden-tel-input'
42795 hiddenInput.name = this.name;
42798 if (this.disabled) {
42799 input.disabled = true;
42802 var flag_container = {
42819 cls: this.hasFeedback ? 'has-feedback' : '',
42825 cls: 'dial-code-holder',
42832 cls: 'roo-select2-container input-group',
42839 if (this.fieldLabel.length) {
42842 tooltip: 'This field is required'
42848 cls: 'control-label',
42854 html: this.fieldLabel
42857 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42863 if(this.indicatorpos == 'right') {
42864 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42871 if(align == 'left') {
42879 if(this.labelWidth > 12){
42880 label.style = "width: " + this.labelWidth + 'px';
42882 if(this.labelWidth < 13 && this.labelmd == 0){
42883 this.labelmd = this.labelWidth;
42885 if(this.labellg > 0){
42886 label.cls += ' col-lg-' + this.labellg;
42887 input.cls += ' col-lg-' + (12 - this.labellg);
42889 if(this.labelmd > 0){
42890 label.cls += ' col-md-' + this.labelmd;
42891 container.cls += ' col-md-' + (12 - this.labelmd);
42893 if(this.labelsm > 0){
42894 label.cls += ' col-sm-' + this.labelsm;
42895 container.cls += ' col-sm-' + (12 - this.labelsm);
42897 if(this.labelxs > 0){
42898 label.cls += ' col-xs-' + this.labelxs;
42899 container.cls += ' col-xs-' + (12 - this.labelxs);
42909 var settings = this;
42911 ['xs','sm','md','lg'].map(function(size){
42912 if (settings[size]) {
42913 cfg.cls += ' col-' + size + '-' + settings[size];
42917 this.store = new Roo.data.Store({
42918 proxy : new Roo.data.MemoryProxy({}),
42919 reader : new Roo.data.JsonReader({
42930 'name' : 'dialCode',
42934 'name' : 'priority',
42938 'name' : 'areaCodes',
42945 if(!this.preferedCountries) {
42946 this.preferedCountries = [
42953 var p = this.preferedCountries.reverse();
42956 for (var i = 0; i < p.length; i++) {
42957 for (var j = 0; j < this.allCountries.length; j++) {
42958 if(this.allCountries[j].iso2 == p[i]) {
42959 var t = this.allCountries[j];
42960 this.allCountries.splice(j,1);
42961 this.allCountries.unshift(t);
42967 this.store.proxy.data = {
42969 data: this.allCountries
42975 initEvents : function()
42978 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42980 this.indicator = this.indicatorEl();
42981 this.flag = this.flagEl();
42982 this.dialCodeHolder = this.dialCodeHolderEl();
42984 this.trigger = this.el.select('div.flag-box',true).first();
42985 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42990 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42991 _this.list.setWidth(lw);
42994 this.list.on('mouseover', this.onViewOver, this);
42995 this.list.on('mousemove', this.onViewMove, this);
42996 this.inputEl().on("keyup", this.onKeyUp, this);
42997 this.inputEl().on("keypress", this.onKeyPress, this);
42999 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43001 this.view = new Roo.View(this.list, this.tpl, {
43002 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43005 this.view.on('click', this.onViewClick, this);
43006 this.setValue(this.defaultDialCode);
43009 onTriggerClick : function(e)
43011 Roo.log('trigger click');
43016 if(this.isExpanded()){
43018 this.hasFocus = false;
43020 this.store.load({});
43021 this.hasFocus = true;
43026 isExpanded : function()
43028 return this.list.isVisible();
43031 collapse : function()
43033 if(!this.isExpanded()){
43037 Roo.get(document).un('mousedown', this.collapseIf, this);
43038 Roo.get(document).un('mousewheel', this.collapseIf, this);
43039 this.fireEvent('collapse', this);
43043 expand : function()
43047 if(this.isExpanded() || !this.hasFocus){
43051 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43052 this.list.setWidth(lw);
43055 this.restrictHeight();
43057 Roo.get(document).on('mousedown', this.collapseIf, this);
43058 Roo.get(document).on('mousewheel', this.collapseIf, this);
43060 this.fireEvent('expand', this);
43063 restrictHeight : function()
43065 this.list.alignTo(this.inputEl(), this.listAlign);
43066 this.list.alignTo(this.inputEl(), this.listAlign);
43069 onViewOver : function(e, t)
43071 if(this.inKeyMode){
43074 var item = this.view.findItemFromChild(t);
43077 var index = this.view.indexOf(item);
43078 this.select(index, false);
43083 onViewClick : function(view, doFocus, el, e)
43085 var index = this.view.getSelectedIndexes()[0];
43087 var r = this.store.getAt(index);
43090 this.onSelect(r, index);
43092 if(doFocus !== false && !this.blockFocus){
43093 this.inputEl().focus();
43097 onViewMove : function(e, t)
43099 this.inKeyMode = false;
43102 select : function(index, scrollIntoView)
43104 this.selectedIndex = index;
43105 this.view.select(index);
43106 if(scrollIntoView !== false){
43107 var el = this.view.getNode(index);
43109 this.list.scrollChildIntoView(el, false);
43114 createList : function()
43116 this.list = Roo.get(document.body).createChild({
43118 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43119 style: 'display:none'
43122 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43125 collapseIf : function(e)
43127 var in_combo = e.within(this.el);
43128 var in_list = e.within(this.list);
43129 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43131 if (in_combo || in_list || is_list) {
43137 onSelect : function(record, index)
43139 if(this.fireEvent('beforeselect', this, record, index) !== false){
43141 this.setFlagClass(record.data.iso2);
43142 this.setDialCode(record.data.dialCode);
43143 this.hasFocus = false;
43145 this.fireEvent('select', this, record, index);
43149 flagEl : function()
43151 var flag = this.el.select('div.flag',true).first();
43158 dialCodeHolderEl : function()
43160 var d = this.el.select('input.dial-code-holder',true).first();
43167 setDialCode : function(v)
43169 this.dialCodeHolder.dom.value = '+'+v;
43172 setFlagClass : function(n)
43174 this.flag.dom.className = 'flag '+n;
43177 getValue : function()
43179 var v = this.inputEl().getValue();
43180 if(this.dialCodeHolder) {
43181 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43186 setValue : function(v)
43188 var d = this.getDialCode(v);
43190 //invalid dial code
43191 if(v.length == 0 || !d || d.length == 0) {
43193 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43194 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43200 this.setFlagClass(this.dialCodeMapping[d].iso2);
43201 this.setDialCode(d);
43202 this.inputEl().dom.value = v.replace('+'+d,'');
43203 this.hiddenEl().dom.value = this.getValue();
43208 getDialCode : function(v)
43212 if (v.length == 0) {
43213 return this.dialCodeHolder.dom.value;
43217 if (v.charAt(0) != "+") {
43220 var numericChars = "";
43221 for (var i = 1; i < v.length; i++) {
43222 var c = v.charAt(i);
43225 if (this.dialCodeMapping[numericChars]) {
43226 dialCode = v.substr(1, i);
43228 if (numericChars.length == 4) {
43238 this.setValue(this.defaultDialCode);
43242 hiddenEl : function()
43244 return this.el.select('input.hidden-tel-input',true).first();
43247 // after setting val
43248 onKeyUp : function(e){
43249 this.setValue(this.getValue());
43252 onKeyPress : function(e){
43253 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43260 * @class Roo.bootstrap.MoneyField
43261 * @extends Roo.bootstrap.ComboBox
43262 * Bootstrap MoneyField class
43265 * Create a new MoneyField.
43266 * @param {Object} config Configuration options
43269 Roo.bootstrap.MoneyField = function(config) {
43271 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43275 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43278 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43280 allowDecimals : true,
43282 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43284 decimalSeparator : ".",
43286 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43288 decimalPrecision : 0,
43290 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43292 allowNegative : true,
43294 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43298 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43300 minValue : Number.NEGATIVE_INFINITY,
43302 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43304 maxValue : Number.MAX_VALUE,
43306 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43308 minText : "The minimum value for this field is {0}",
43310 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43312 maxText : "The maximum value for this field is {0}",
43314 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43315 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43317 nanText : "{0} is not a valid number",
43319 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43323 * @cfg {String} defaults currency of the MoneyField
43324 * value should be in lkey
43326 defaultCurrency : false,
43328 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43330 thousandsDelimiter : false,
43332 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43343 getAutoCreate : function()
43345 var align = this.labelAlign || this.parentLabelAlign();
43357 cls : 'form-control roo-money-amount-input',
43358 autocomplete: 'new-password'
43361 var hiddenInput = {
43365 cls: 'hidden-number-input'
43368 if(this.max_length) {
43369 input.maxlength = this.max_length;
43373 hiddenInput.name = this.name;
43376 if (this.disabled) {
43377 input.disabled = true;
43380 var clg = 12 - this.inputlg;
43381 var cmd = 12 - this.inputmd;
43382 var csm = 12 - this.inputsm;
43383 var cxs = 12 - this.inputxs;
43387 cls : 'row roo-money-field',
43391 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43395 cls: 'roo-select2-container input-group',
43399 cls : 'form-control roo-money-currency-input',
43400 autocomplete: 'new-password',
43402 name : this.currencyName
43406 cls : 'input-group-addon',
43420 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43424 cls: this.hasFeedback ? 'has-feedback' : '',
43435 if (this.fieldLabel.length) {
43438 tooltip: 'This field is required'
43444 cls: 'control-label',
43450 html: this.fieldLabel
43453 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43459 if(this.indicatorpos == 'right') {
43460 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43467 if(align == 'left') {
43475 if(this.labelWidth > 12){
43476 label.style = "width: " + this.labelWidth + 'px';
43478 if(this.labelWidth < 13 && this.labelmd == 0){
43479 this.labelmd = this.labelWidth;
43481 if(this.labellg > 0){
43482 label.cls += ' col-lg-' + this.labellg;
43483 input.cls += ' col-lg-' + (12 - this.labellg);
43485 if(this.labelmd > 0){
43486 label.cls += ' col-md-' + this.labelmd;
43487 container.cls += ' col-md-' + (12 - this.labelmd);
43489 if(this.labelsm > 0){
43490 label.cls += ' col-sm-' + this.labelsm;
43491 container.cls += ' col-sm-' + (12 - this.labelsm);
43493 if(this.labelxs > 0){
43494 label.cls += ' col-xs-' + this.labelxs;
43495 container.cls += ' col-xs-' + (12 - this.labelxs);
43506 var settings = this;
43508 ['xs','sm','md','lg'].map(function(size){
43509 if (settings[size]) {
43510 cfg.cls += ' col-' + size + '-' + settings[size];
43517 initEvents : function()
43519 this.indicator = this.indicatorEl();
43521 this.initCurrencyEvent();
43523 this.initNumberEvent();
43526 initCurrencyEvent : function()
43529 throw "can not find store for combo";
43532 this.store = Roo.factory(this.store, Roo.data);
43533 this.store.parent = this;
43537 this.triggerEl = this.el.select('.input-group-addon', true).first();
43539 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43544 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43545 _this.list.setWidth(lw);
43548 this.list.on('mouseover', this.onViewOver, this);
43549 this.list.on('mousemove', this.onViewMove, this);
43550 this.list.on('scroll', this.onViewScroll, this);
43553 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43556 this.view = new Roo.View(this.list, this.tpl, {
43557 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43560 this.view.on('click', this.onViewClick, this);
43562 this.store.on('beforeload', this.onBeforeLoad, this);
43563 this.store.on('load', this.onLoad, this);
43564 this.store.on('loadexception', this.onLoadException, this);
43566 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43567 "up" : function(e){
43568 this.inKeyMode = true;
43572 "down" : function(e){
43573 if(!this.isExpanded()){
43574 this.onTriggerClick();
43576 this.inKeyMode = true;
43581 "enter" : function(e){
43584 if(this.fireEvent("specialkey", this, e)){
43585 this.onViewClick(false);
43591 "esc" : function(e){
43595 "tab" : function(e){
43598 if(this.fireEvent("specialkey", this, e)){
43599 this.onViewClick(false);
43607 doRelay : function(foo, bar, hname){
43608 if(hname == 'down' || this.scope.isExpanded()){
43609 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43617 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43621 initNumberEvent : function(e)
43623 this.inputEl().on("keydown" , this.fireKey, this);
43624 this.inputEl().on("focus", this.onFocus, this);
43625 this.inputEl().on("blur", this.onBlur, this);
43627 this.inputEl().relayEvent('keyup', this);
43629 if(this.indicator){
43630 this.indicator.addClass('invisible');
43633 this.originalValue = this.getValue();
43635 if(this.validationEvent == 'keyup'){
43636 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43637 this.inputEl().on('keyup', this.filterValidation, this);
43639 else if(this.validationEvent !== false){
43640 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43643 if(this.selectOnFocus){
43644 this.on("focus", this.preFocus, this);
43647 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43648 this.inputEl().on("keypress", this.filterKeys, this);
43650 this.inputEl().relayEvent('keypress', this);
43653 var allowed = "0123456789";
43655 if(this.allowDecimals){
43656 allowed += this.decimalSeparator;
43659 if(this.allowNegative){
43663 if(this.thousandsDelimiter) {
43667 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43669 var keyPress = function(e){
43671 var k = e.getKey();
43673 var c = e.getCharCode();
43676 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43677 allowed.indexOf(String.fromCharCode(c)) === -1
43683 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43687 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43692 this.inputEl().on("keypress", keyPress, this);
43696 onTriggerClick : function(e)
43703 this.loadNext = false;
43705 if(this.isExpanded()){
43710 this.hasFocus = true;
43712 if(this.triggerAction == 'all') {
43713 this.doQuery(this.allQuery, true);
43717 this.doQuery(this.getRawValue());
43720 getCurrency : function()
43722 var v = this.currencyEl().getValue();
43727 restrictHeight : function()
43729 this.list.alignTo(this.currencyEl(), this.listAlign);
43730 this.list.alignTo(this.currencyEl(), this.listAlign);
43733 onViewClick : function(view, doFocus, el, e)
43735 var index = this.view.getSelectedIndexes()[0];
43737 var r = this.store.getAt(index);
43740 this.onSelect(r, index);
43744 onSelect : function(record, index){
43746 if(this.fireEvent('beforeselect', this, record, index) !== false){
43748 this.setFromCurrencyData(index > -1 ? record.data : false);
43752 this.fireEvent('select', this, record, index);
43756 setFromCurrencyData : function(o)
43760 this.lastCurrency = o;
43762 if (this.currencyField) {
43763 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43765 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43768 this.lastSelectionText = currency;
43770 //setting default currency
43771 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43772 this.setCurrency(this.defaultCurrency);
43776 this.setCurrency(currency);
43779 setFromData : function(o)
43783 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43785 this.setFromCurrencyData(c);
43790 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43792 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43795 this.setValue(value);
43799 setCurrency : function(v)
43801 this.currencyValue = v;
43804 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43809 setValue : function(v)
43811 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43817 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43819 this.inputEl().dom.value = (v == '') ? '' :
43820 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43822 if(!this.allowZero && v === '0') {
43823 this.hiddenEl().dom.value = '';
43824 this.inputEl().dom.value = '';
43831 getRawValue : function()
43833 var v = this.inputEl().getValue();
43838 getValue : function()
43840 return this.fixPrecision(this.parseValue(this.getRawValue()));
43843 parseValue : function(value)
43845 if(this.thousandsDelimiter) {
43847 r = new RegExp(",", "g");
43848 value = value.replace(r, "");
43851 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43852 return isNaN(value) ? '' : value;
43856 fixPrecision : function(value)
43858 if(this.thousandsDelimiter) {
43860 r = new RegExp(",", "g");
43861 value = value.replace(r, "");
43864 var nan = isNaN(value);
43866 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43867 return nan ? '' : value;
43869 return parseFloat(value).toFixed(this.decimalPrecision);
43872 decimalPrecisionFcn : function(v)
43874 return Math.floor(v);
43877 validateValue : function(value)
43879 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43883 var num = this.parseValue(value);
43886 this.markInvalid(String.format(this.nanText, value));
43890 if(num < this.minValue){
43891 this.markInvalid(String.format(this.minText, this.minValue));
43895 if(num > this.maxValue){
43896 this.markInvalid(String.format(this.maxText, this.maxValue));
43903 validate : function()
43905 if(this.disabled || this.allowBlank){
43910 var currency = this.getCurrency();
43912 if(this.validateValue(this.getRawValue()) && currency.length){
43917 this.markInvalid();
43921 getName: function()
43926 beforeBlur : function()
43932 var v = this.parseValue(this.getRawValue());
43939 onBlur : function()
43943 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43944 //this.el.removeClass(this.focusClass);
43947 this.hasFocus = false;
43949 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43953 var v = this.getValue();
43955 if(String(v) !== String(this.startValue)){
43956 this.fireEvent('change', this, v, this.startValue);
43959 this.fireEvent("blur", this);
43962 inputEl : function()
43964 return this.el.select('.roo-money-amount-input', true).first();
43967 currencyEl : function()
43969 return this.el.select('.roo-money-currency-input', true).first();
43972 hiddenEl : function()
43974 return this.el.select('input.hidden-number-input',true).first();
43978 * @class Roo.bootstrap.BezierSignature
43979 * @extends Roo.bootstrap.Component
43980 * Bootstrap BezierSignature class
43981 * This script refer to:
43982 * Title: Signature Pad
43984 * Availability: https://github.com/szimek/signature_pad
43987 * Create a new BezierSignature
43988 * @param {Object} config The config object
43991 Roo.bootstrap.BezierSignature = function(config){
43992 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43998 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44005 mouse_btn_down: true,
44008 * @cfg {int} canvas height
44010 canvas_height: '200px',
44013 * @cfg {float|function} Radius of a single dot.
44018 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44023 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44028 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44033 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44038 * @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.
44040 bg_color: 'rgba(0, 0, 0, 0)',
44043 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44045 dot_color: 'black',
44048 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44050 velocity_filter_weight: 0.7,
44053 * @cfg {function} Callback when stroke begin.
44058 * @cfg {function} Callback when stroke end.
44062 getAutoCreate : function()
44064 var cls = 'roo-signature column';
44067 cls += ' ' + this.cls;
44077 for(var i = 0; i < col_sizes.length; i++) {
44078 if(this[col_sizes[i]]) {
44079 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44089 cls: 'roo-signature-body',
44093 cls: 'roo-signature-body-canvas',
44094 height: this.canvas_height,
44095 width: this.canvas_width
44102 style: 'display: none'
44110 initEvents: function()
44112 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44114 var canvas = this.canvasEl();
44116 // mouse && touch event swapping...
44117 canvas.dom.style.touchAction = 'none';
44118 canvas.dom.style.msTouchAction = 'none';
44120 this.mouse_btn_down = false;
44121 canvas.on('mousedown', this._handleMouseDown, this);
44122 canvas.on('mousemove', this._handleMouseMove, this);
44123 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44125 if (window.PointerEvent) {
44126 canvas.on('pointerdown', this._handleMouseDown, this);
44127 canvas.on('pointermove', this._handleMouseMove, this);
44128 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44131 if ('ontouchstart' in window) {
44132 canvas.on('touchstart', this._handleTouchStart, this);
44133 canvas.on('touchmove', this._handleTouchMove, this);
44134 canvas.on('touchend', this._handleTouchEnd, this);
44137 Roo.EventManager.onWindowResize(this.resize, this, true);
44139 // file input event
44140 this.fileEl().on('change', this.uploadImage, this);
44147 resize: function(){
44149 var canvas = this.canvasEl().dom;
44150 var ctx = this.canvasElCtx();
44151 var img_data = false;
44153 if(canvas.width > 0) {
44154 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44156 // setting canvas width will clean img data
44159 var style = window.getComputedStyle ?
44160 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44162 var padding_left = parseInt(style.paddingLeft) || 0;
44163 var padding_right = parseInt(style.paddingRight) || 0;
44165 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44168 ctx.putImageData(img_data, 0, 0);
44172 _handleMouseDown: function(e)
44174 if (e.browserEvent.which === 1) {
44175 this.mouse_btn_down = true;
44176 this.strokeBegin(e);
44180 _handleMouseMove: function (e)
44182 if (this.mouse_btn_down) {
44183 this.strokeMoveUpdate(e);
44187 _handleMouseUp: function (e)
44189 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44190 this.mouse_btn_down = false;
44195 _handleTouchStart: function (e) {
44197 e.preventDefault();
44198 if (e.browserEvent.targetTouches.length === 1) {
44199 // var touch = e.browserEvent.changedTouches[0];
44200 // this.strokeBegin(touch);
44202 this.strokeBegin(e); // assume e catching the correct xy...
44206 _handleTouchMove: function (e) {
44207 e.preventDefault();
44208 // var touch = event.targetTouches[0];
44209 // _this._strokeMoveUpdate(touch);
44210 this.strokeMoveUpdate(e);
44213 _handleTouchEnd: function (e) {
44214 var wasCanvasTouched = e.target === this.canvasEl().dom;
44215 if (wasCanvasTouched) {
44216 e.preventDefault();
44217 // var touch = event.changedTouches[0];
44218 // _this._strokeEnd(touch);
44223 reset: function () {
44224 this._lastPoints = [];
44225 this._lastVelocity = 0;
44226 this._lastWidth = (this.min_width + this.max_width) / 2;
44227 this.canvasElCtx().fillStyle = this.dot_color;
44230 strokeMoveUpdate: function(e)
44232 this.strokeUpdate(e);
44234 if (this.throttle) {
44235 this.throttleStroke(this.strokeUpdate, this.throttle);
44238 this.strokeUpdate(e);
44242 strokeBegin: function(e)
44244 var newPointGroup = {
44245 color: this.dot_color,
44249 if (typeof this.onBegin === 'function') {
44253 this.curve_data.push(newPointGroup);
44255 this.strokeUpdate(e);
44258 strokeUpdate: function(e)
44260 var rect = this.canvasEl().dom.getBoundingClientRect();
44261 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44262 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44263 var lastPoints = lastPointGroup.points;
44264 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44265 var isLastPointTooClose = lastPoint
44266 ? point.distanceTo(lastPoint) <= this.min_distance
44268 var color = lastPointGroup.color;
44269 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44270 var curve = this.addPoint(point);
44272 this.drawDot({color: color, point: point});
44275 this.drawCurve({color: color, curve: curve});
44285 strokeEnd: function(e)
44287 this.strokeUpdate(e);
44288 if (typeof this.onEnd === 'function') {
44293 addPoint: function (point) {
44294 var _lastPoints = this._lastPoints;
44295 _lastPoints.push(point);
44296 if (_lastPoints.length > 2) {
44297 if (_lastPoints.length === 3) {
44298 _lastPoints.unshift(_lastPoints[0]);
44300 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44301 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44302 _lastPoints.shift();
44308 calculateCurveWidths: function (startPoint, endPoint) {
44309 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44310 (1 - this.velocity_filter_weight) * this._lastVelocity;
44312 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44315 start: this._lastWidth
44318 this._lastVelocity = velocity;
44319 this._lastWidth = newWidth;
44323 drawDot: function (_a) {
44324 var color = _a.color, point = _a.point;
44325 var ctx = this.canvasElCtx();
44326 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44328 this.drawCurveSegment(point.x, point.y, width);
44330 ctx.fillStyle = color;
44334 drawCurve: function (_a) {
44335 var color = _a.color, curve = _a.curve;
44336 var ctx = this.canvasElCtx();
44337 var widthDelta = curve.endWidth - curve.startWidth;
44338 var drawSteps = Math.floor(curve.length()) * 2;
44340 ctx.fillStyle = color;
44341 for (var i = 0; i < drawSteps; i += 1) {
44342 var t = i / drawSteps;
44348 var x = uuu * curve.startPoint.x;
44349 x += 3 * uu * t * curve.control1.x;
44350 x += 3 * u * tt * curve.control2.x;
44351 x += ttt * curve.endPoint.x;
44352 var y = uuu * curve.startPoint.y;
44353 y += 3 * uu * t * curve.control1.y;
44354 y += 3 * u * tt * curve.control2.y;
44355 y += ttt * curve.endPoint.y;
44356 var width = curve.startWidth + ttt * widthDelta;
44357 this.drawCurveSegment(x, y, width);
44363 drawCurveSegment: function (x, y, width) {
44364 var ctx = this.canvasElCtx();
44366 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44367 this.is_empty = false;
44372 var ctx = this.canvasElCtx();
44373 var canvas = this.canvasEl().dom;
44374 ctx.fillStyle = this.bg_color;
44375 ctx.clearRect(0, 0, canvas.width, canvas.height);
44376 ctx.fillRect(0, 0, canvas.width, canvas.height);
44377 this.curve_data = [];
44379 this.is_empty = true;
44384 return this.el.select('input',true).first();
44387 canvasEl: function()
44389 return this.el.select('canvas',true).first();
44392 canvasElCtx: function()
44394 return this.el.select('canvas',true).first().dom.getContext('2d');
44397 getImage: function(type)
44399 if(this.is_empty) {
44404 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44407 drawFromImage: function(img_src)
44409 var img = new Image();
44411 img.onload = function(){
44412 this.canvasElCtx().drawImage(img, 0, 0);
44417 this.is_empty = false;
44420 selectImage: function()
44422 this.fileEl().dom.click();
44425 uploadImage: function(e)
44427 var reader = new FileReader();
44429 reader.onload = function(e){
44430 var img = new Image();
44431 img.onload = function(){
44433 this.canvasElCtx().drawImage(img, 0, 0);
44435 img.src = e.target.result;
44438 reader.readAsDataURL(e.target.files[0]);
44441 // Bezier Point Constructor
44442 Point: (function () {
44443 function Point(x, y, time) {
44446 this.time = time || Date.now();
44448 Point.prototype.distanceTo = function (start) {
44449 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44451 Point.prototype.equals = function (other) {
44452 return this.x === other.x && this.y === other.y && this.time === other.time;
44454 Point.prototype.velocityFrom = function (start) {
44455 return this.time !== start.time
44456 ? this.distanceTo(start) / (this.time - start.time)
44463 // Bezier Constructor
44464 Bezier: (function () {
44465 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44466 this.startPoint = startPoint;
44467 this.control2 = control2;
44468 this.control1 = control1;
44469 this.endPoint = endPoint;
44470 this.startWidth = startWidth;
44471 this.endWidth = endWidth;
44473 Bezier.fromPoints = function (points, widths, scope) {
44474 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44475 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44476 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44478 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44479 var dx1 = s1.x - s2.x;
44480 var dy1 = s1.y - s2.y;
44481 var dx2 = s2.x - s3.x;
44482 var dy2 = s2.y - s3.y;
44483 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44484 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44485 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44486 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44487 var dxm = m1.x - m2.x;
44488 var dym = m1.y - m2.y;
44489 var k = l2 / (l1 + l2);
44490 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44491 var tx = s2.x - cm.x;
44492 var ty = s2.y - cm.y;
44494 c1: new scope.Point(m1.x + tx, m1.y + ty),
44495 c2: new scope.Point(m2.x + tx, m2.y + ty)
44498 Bezier.prototype.length = function () {
44503 for (var i = 0; i <= steps; i += 1) {
44505 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44506 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44508 var xdiff = cx - px;
44509 var ydiff = cy - py;
44510 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44517 Bezier.prototype.point = function (t, start, c1, c2, end) {
44518 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44519 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44520 + (3.0 * c2 * (1.0 - t) * t * t)
44521 + (end * t * t * t);
44526 throttleStroke: function(fn, wait) {
44527 if (wait === void 0) { wait = 250; }
44529 var timeout = null;
44533 var later = function () {
44534 previous = Date.now();
44536 result = fn.apply(storedContext, storedArgs);
44538 storedContext = null;
44542 return function wrapper() {
44544 for (var _i = 0; _i < arguments.length; _i++) {
44545 args[_i] = arguments[_i];
44547 var now = Date.now();
44548 var remaining = wait - (now - previous);
44549 storedContext = this;
44551 if (remaining <= 0 || remaining > wait) {
44553 clearTimeout(timeout);
44557 result = fn.apply(storedContext, storedArgs);
44559 storedContext = null;
44563 else if (!timeout) {
44564 timeout = window.setTimeout(later, remaining);