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, [ this.header_imageEl ]);
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.dom.naturalHeight / he.dom.naturalWidth;
2699 //var w = he.dom.naturalWidth;
2700 var ww = he.dom.width;
2702 he.setSize( ww * (1/hw), ww);
2713 * Card header - holder for the card header elements.
2718 * @class Roo.bootstrap.CardHeader
2719 * @extends Roo.bootstrap.Element
2720 * Bootstrap CardHeader class
2722 * Create a new Card Header - that you can embed children into
2723 * @param {Object} config The config object
2726 Roo.bootstrap.CardHeader = function(config){
2727 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2730 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2733 container_method : 'getCardHeader'
2746 * Card footer - holder for the card footer elements.
2751 * @class Roo.bootstrap.CardFooter
2752 * @extends Roo.bootstrap.Element
2753 * Bootstrap CardFooter class
2755 * Create a new Card Footer - that you can embed children into
2756 * @param {Object} config The config object
2759 Roo.bootstrap.CardFooter = function(config){
2760 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2763 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2766 container_method : 'getCardFooter'
2779 * Card header - holder for the card header elements.
2784 * @class Roo.bootstrap.CardImageTop
2785 * @extends Roo.bootstrap.Element
2786 * Bootstrap CardImageTop class
2788 * Create a new Card Image Top container
2789 * @param {Object} config The config object
2792 Roo.bootstrap.CardImageTop = function(config){
2793 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2796 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2799 container_method : 'getCardImageTop'
2817 * @class Roo.bootstrap.Img
2818 * @extends Roo.bootstrap.Component
2819 * Bootstrap Img class
2820 * @cfg {Boolean} imgResponsive false | true
2821 * @cfg {String} border rounded | circle | thumbnail
2822 * @cfg {String} src image source
2823 * @cfg {String} alt image alternative text
2824 * @cfg {String} href a tag href
2825 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2826 * @cfg {String} xsUrl xs image source
2827 * @cfg {String} smUrl sm image source
2828 * @cfg {String} mdUrl md image source
2829 * @cfg {String} lgUrl lg image source
2832 * Create a new Input
2833 * @param {Object} config The config object
2836 Roo.bootstrap.Img = function(config){
2837 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2843 * The img click event for the img.
2844 * @param {Roo.EventObject} e
2850 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2852 imgResponsive: true,
2862 getAutoCreate : function()
2864 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2865 return this.createSingleImg();
2870 cls: 'roo-image-responsive-group',
2875 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2877 if(!_this[size + 'Url']){
2883 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2884 html: _this.html || cfg.html,
2885 src: _this[size + 'Url']
2888 img.cls += ' roo-image-responsive-' + size;
2890 var s = ['xs', 'sm', 'md', 'lg'];
2892 s.splice(s.indexOf(size), 1);
2894 Roo.each(s, function(ss){
2895 img.cls += ' hidden-' + ss;
2898 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2899 cfg.cls += ' img-' + _this.border;
2903 cfg.alt = _this.alt;
2916 a.target = _this.target;
2920 cfg.cn.push((_this.href) ? a : img);
2927 createSingleImg : function()
2931 cls: (this.imgResponsive) ? 'img-responsive' : '',
2933 src : 'about:blank' // just incase src get's set to undefined?!?
2936 cfg.html = this.html || cfg.html;
2938 cfg.src = this.src || cfg.src;
2940 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2941 cfg.cls += ' img-' + this.border;
2958 a.target = this.target;
2963 return (this.href) ? a : cfg;
2966 initEvents: function()
2969 this.el.on('click', this.onClick, this);
2974 onClick : function(e)
2976 Roo.log('img onclick');
2977 this.fireEvent('click', this, e);
2980 * Sets the url of the image - used to update it
2981 * @param {String} url the url of the image
2984 setSrc : function(url)
2988 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2989 this.el.dom.src = url;
2993 this.el.select('img', true).first().dom.src = url;
3009 * @class Roo.bootstrap.Link
3010 * @extends Roo.bootstrap.Component
3011 * Bootstrap Link Class
3012 * @cfg {String} alt image alternative text
3013 * @cfg {String} href a tag href
3014 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3015 * @cfg {String} html the content of the link.
3016 * @cfg {String} anchor name for the anchor link
3017 * @cfg {String} fa - favicon
3019 * @cfg {Boolean} preventDefault (true | false) default false
3023 * Create a new Input
3024 * @param {Object} config The config object
3027 Roo.bootstrap.Link = function(config){
3028 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3034 * The img click event for the img.
3035 * @param {Roo.EventObject} e
3041 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3045 preventDefault: false,
3051 getAutoCreate : function()
3053 var html = this.html || '';
3055 if (this.fa !== false) {
3056 html = '<i class="fa fa-' + this.fa + '"></i>';
3061 // anchor's do not require html/href...
3062 if (this.anchor === false) {
3064 cfg.href = this.href || '#';
3066 cfg.name = this.anchor;
3067 if (this.html !== false || this.fa !== false) {
3070 if (this.href !== false) {
3071 cfg.href = this.href;
3075 if(this.alt !== false){
3080 if(this.target !== false) {
3081 cfg.target = this.target;
3087 initEvents: function() {
3089 if(!this.href || this.preventDefault){
3090 this.el.on('click', this.onClick, this);
3094 onClick : function(e)
3096 if(this.preventDefault){
3099 //Roo.log('img onclick');
3100 this.fireEvent('click', this, e);
3113 * @class Roo.bootstrap.Header
3114 * @extends Roo.bootstrap.Component
3115 * Bootstrap Header class
3116 * @cfg {String} html content of header
3117 * @cfg {Number} level (1|2|3|4|5|6) default 1
3120 * Create a new Header
3121 * @param {Object} config The config object
3125 Roo.bootstrap.Header = function(config){
3126 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3129 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3137 getAutoCreate : function(){
3142 tag: 'h' + (1 *this.level),
3143 html: this.html || ''
3155 * Ext JS Library 1.1.1
3156 * Copyright(c) 2006-2007, Ext JS, LLC.
3158 * Originally Released Under LGPL - original licence link has changed is not relivant.
3161 * <script type="text/javascript">
3165 * @class Roo.bootstrap.MenuMgr
3166 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3169 Roo.bootstrap.MenuMgr = function(){
3170 var menus, active, groups = {}, attached = false, lastShow = new Date();
3172 // private - called when first menu is created
3175 active = new Roo.util.MixedCollection();
3176 Roo.get(document).addKeyListener(27, function(){
3177 if(active.length > 0){
3185 if(active && active.length > 0){
3186 var c = active.clone();
3196 if(active.length < 1){
3197 Roo.get(document).un("mouseup", onMouseDown);
3205 var last = active.last();
3206 lastShow = new Date();
3209 Roo.get(document).on("mouseup", onMouseDown);
3214 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3215 m.parentMenu.activeChild = m;
3216 }else if(last && last.isVisible()){
3217 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3222 function onBeforeHide(m){
3224 m.activeChild.hide();
3226 if(m.autoHideTimer){
3227 clearTimeout(m.autoHideTimer);
3228 delete m.autoHideTimer;
3233 function onBeforeShow(m){
3234 var pm = m.parentMenu;
3235 if(!pm && !m.allowOtherMenus){
3237 }else if(pm && pm.activeChild && active != m){
3238 pm.activeChild.hide();
3242 // private this should really trigger on mouseup..
3243 function onMouseDown(e){
3244 Roo.log("on Mouse Up");
3246 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3247 Roo.log("MenuManager hideAll");
3256 function onBeforeCheck(mi, state){
3258 var g = groups[mi.group];
3259 for(var i = 0, l = g.length; i < l; i++){
3261 g[i].setChecked(false);
3270 * Hides all menus that are currently visible
3272 hideAll : function(){
3277 register : function(menu){
3281 menus[menu.id] = menu;
3282 menu.on("beforehide", onBeforeHide);
3283 menu.on("hide", onHide);
3284 menu.on("beforeshow", onBeforeShow);
3285 menu.on("show", onShow);
3287 if(g && menu.events["checkchange"]){
3291 groups[g].push(menu);
3292 menu.on("checkchange", onCheck);
3297 * Returns a {@link Roo.menu.Menu} object
3298 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3299 * be used to generate and return a new Menu instance.
3301 get : function(menu){
3302 if(typeof menu == "string"){ // menu id
3304 }else if(menu.events){ // menu instance
3307 /*else if(typeof menu.length == 'number'){ // array of menu items?
3308 return new Roo.bootstrap.Menu({items:menu});
3309 }else{ // otherwise, must be a config
3310 return new Roo.bootstrap.Menu(menu);
3317 unregister : function(menu){
3318 delete menus[menu.id];
3319 menu.un("beforehide", onBeforeHide);
3320 menu.un("hide", onHide);
3321 menu.un("beforeshow", onBeforeShow);
3322 menu.un("show", onShow);
3324 if(g && menu.events["checkchange"]){
3325 groups[g].remove(menu);
3326 menu.un("checkchange", onCheck);
3331 registerCheckable : function(menuItem){
3332 var g = menuItem.group;
3337 groups[g].push(menuItem);
3338 menuItem.on("beforecheckchange", onBeforeCheck);
3343 unregisterCheckable : function(menuItem){
3344 var g = menuItem.group;
3346 groups[g].remove(menuItem);
3347 menuItem.un("beforecheckchange", onBeforeCheck);
3359 * @class Roo.bootstrap.Menu
3360 * @extends Roo.bootstrap.Component
3361 * Bootstrap Menu class - container for MenuItems
3362 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3363 * @cfg {bool} hidden if the menu should be hidden when rendered.
3364 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3365 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3369 * @param {Object} config The config object
3373 Roo.bootstrap.Menu = function(config){
3374 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3375 if (this.registerMenu && this.type != 'treeview') {
3376 Roo.bootstrap.MenuMgr.register(this);
3383 * Fires before this menu is displayed (return false to block)
3384 * @param {Roo.menu.Menu} this
3389 * Fires before this menu is hidden (return false to block)
3390 * @param {Roo.menu.Menu} this
3395 * Fires after this menu is displayed
3396 * @param {Roo.menu.Menu} this
3401 * Fires after this menu is hidden
3402 * @param {Roo.menu.Menu} this
3407 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3408 * @param {Roo.menu.Menu} this
3409 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3410 * @param {Roo.EventObject} e
3415 * Fires when the mouse is hovering over this menu
3416 * @param {Roo.menu.Menu} this
3417 * @param {Roo.EventObject} e
3418 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3423 * Fires when the mouse exits this menu
3424 * @param {Roo.menu.Menu} this
3425 * @param {Roo.EventObject} e
3426 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3431 * Fires when a menu item contained in this menu is clicked
3432 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3433 * @param {Roo.EventObject} e
3437 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3440 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3444 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3447 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3449 registerMenu : true,
3451 menuItems :false, // stores the menu items..
3461 getChildContainer : function() {
3465 getAutoCreate : function(){
3467 //if (['right'].indexOf(this.align)!==-1) {
3468 // cfg.cn[1].cls += ' pull-right'
3474 cls : 'dropdown-menu' ,
3475 style : 'z-index:1000'
3479 if (this.type === 'submenu') {
3480 cfg.cls = 'submenu active';
3482 if (this.type === 'treeview') {
3483 cfg.cls = 'treeview-menu';
3488 initEvents : function() {
3490 // Roo.log("ADD event");
3491 // Roo.log(this.triggerEl.dom);
3493 this.triggerEl.on('click', this.onTriggerClick, this);
3495 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3498 if (this.triggerEl.hasClass('nav-item')) {
3499 // dropdown toggle on the 'a' in BS4?
3500 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3502 this.triggerEl.addClass('dropdown-toggle');
3505 this.el.on('touchstart' , this.onTouch, this);
3507 this.el.on('click' , this.onClick, this);
3509 this.el.on("mouseover", this.onMouseOver, this);
3510 this.el.on("mouseout", this.onMouseOut, this);
3514 findTargetItem : function(e)
3516 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3520 //Roo.log(t); Roo.log(t.id);
3522 //Roo.log(this.menuitems);
3523 return this.menuitems.get(t.id);
3525 //return this.items.get(t.menuItemId);
3531 onTouch : function(e)
3533 Roo.log("menu.onTouch");
3534 //e.stopEvent(); this make the user popdown broken
3538 onClick : function(e)
3540 Roo.log("menu.onClick");
3542 var t = this.findTargetItem(e);
3543 if(!t || t.isContainer){
3548 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3549 if(t == this.activeItem && t.shouldDeactivate(e)){
3550 this.activeItem.deactivate();
3551 delete this.activeItem;
3555 this.setActiveItem(t, true);
3563 Roo.log('pass click event');
3567 this.fireEvent("click", this, t, e);
3571 if(!t.href.length || t.href == '#'){
3572 (function() { _this.hide(); }).defer(100);
3577 onMouseOver : function(e){
3578 var t = this.findTargetItem(e);
3581 // if(t.canActivate && !t.disabled){
3582 // this.setActiveItem(t, true);
3586 this.fireEvent("mouseover", this, e, t);
3588 isVisible : function(){
3589 return !this.hidden;
3591 onMouseOut : function(e){
3592 var t = this.findTargetItem(e);
3595 // if(t == this.activeItem && t.shouldDeactivate(e)){
3596 // this.activeItem.deactivate();
3597 // delete this.activeItem;
3600 this.fireEvent("mouseout", this, e, t);
3605 * Displays this menu relative to another element
3606 * @param {String/HTMLElement/Roo.Element} element The element to align to
3607 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3608 * the element (defaults to this.defaultAlign)
3609 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3611 show : function(el, pos, parentMenu)
3613 if (false === this.fireEvent("beforeshow", this)) {
3614 Roo.log("show canceled");
3617 this.parentMenu = parentMenu;
3622 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3625 * Displays this menu at a specific xy position
3626 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3627 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3629 showAt : function(xy, parentMenu, /* private: */_e){
3630 this.parentMenu = parentMenu;
3635 this.fireEvent("beforeshow", this);
3636 //xy = this.el.adjustForConstraints(xy);
3640 this.hideMenuItems();
3641 this.hidden = false;
3642 this.triggerEl.addClass('open');
3643 this.el.addClass('show');
3645 // reassign x when hitting right
3646 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3647 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3650 // reassign y when hitting bottom
3651 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3652 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3655 // but the list may align on trigger left or trigger top... should it be a properity?
3657 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3662 this.fireEvent("show", this);
3668 this.doFocus.defer(50, this);
3672 doFocus : function(){
3674 this.focusEl.focus();
3679 * Hides this menu and optionally all parent menus
3680 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3682 hide : function(deep)
3684 if (false === this.fireEvent("beforehide", this)) {
3685 Roo.log("hide canceled");
3688 this.hideMenuItems();
3689 if(this.el && this.isVisible()){
3691 if(this.activeItem){
3692 this.activeItem.deactivate();
3693 this.activeItem = null;
3695 this.triggerEl.removeClass('open');;
3696 this.el.removeClass('show');
3698 this.fireEvent("hide", this);
3700 if(deep === true && this.parentMenu){
3701 this.parentMenu.hide(true);
3705 onTriggerClick : function(e)
3707 Roo.log('trigger click');
3709 var target = e.getTarget();
3711 Roo.log(target.nodeName.toLowerCase());
3713 if(target.nodeName.toLowerCase() === 'i'){
3719 onTriggerPress : function(e)
3721 Roo.log('trigger press');
3722 //Roo.log(e.getTarget());
3723 // Roo.log(this.triggerEl.dom);
3725 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3726 var pel = Roo.get(e.getTarget());
3727 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3728 Roo.log('is treeview or dropdown?');
3732 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3736 if (this.isVisible()) {
3741 this.show(this.triggerEl, '?', false);
3744 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3751 hideMenuItems : function()
3753 Roo.log("hide Menu Items");
3758 this.el.select('.open',true).each(function(aa) {
3760 aa.removeClass('open');
3764 addxtypeChild : function (tree, cntr) {
3765 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3767 this.menuitems.add(comp);
3779 this.getEl().dom.innerHTML = '';
3780 this.menuitems.clear();
3794 * @class Roo.bootstrap.MenuItem
3795 * @extends Roo.bootstrap.Component
3796 * Bootstrap MenuItem class
3797 * @cfg {String} html the menu label
3798 * @cfg {String} href the link
3799 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3800 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3801 * @cfg {Boolean} active used on sidebars to highlight active itesm
3802 * @cfg {String} fa favicon to show on left of menu item.
3803 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3807 * Create a new MenuItem
3808 * @param {Object} config The config object
3812 Roo.bootstrap.MenuItem = function(config){
3813 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3818 * The raw click event for the entire grid.
3819 * @param {Roo.bootstrap.MenuItem} this
3820 * @param {Roo.EventObject} e
3826 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3830 preventDefault: false,
3831 isContainer : false,
3835 getAutoCreate : function(){
3837 if(this.isContainer){
3840 cls: 'dropdown-menu-item '
3850 cls : 'dropdown-item',
3855 if (this.fa !== false) {
3858 cls : 'fa fa-' + this.fa
3867 cls: 'dropdown-menu-item',
3870 if (this.parent().type == 'treeview') {
3871 cfg.cls = 'treeview-menu';
3874 cfg.cls += ' active';
3879 anc.href = this.href || cfg.cn[0].href ;
3880 ctag.html = this.html || cfg.cn[0].html ;
3884 initEvents: function()
3886 if (this.parent().type == 'treeview') {
3887 this.el.select('a').on('click', this.onClick, this);
3891 this.menu.parentType = this.xtype;
3892 this.menu.triggerEl = this.el;
3893 this.menu = this.addxtype(Roo.apply({}, this.menu));
3897 onClick : function(e)
3899 Roo.log('item on click ');
3901 if(this.preventDefault){
3904 //this.parent().hideMenuItems();
3906 this.fireEvent('click', this, e);
3925 * @class Roo.bootstrap.MenuSeparator
3926 * @extends Roo.bootstrap.Component
3927 * Bootstrap MenuSeparator class
3930 * Create a new MenuItem
3931 * @param {Object} config The config object
3935 Roo.bootstrap.MenuSeparator = function(config){
3936 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3939 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3941 getAutoCreate : function(){
3960 * @class Roo.bootstrap.Modal
3961 * @extends Roo.bootstrap.Component
3962 * Bootstrap Modal class
3963 * @cfg {String} title Title of dialog
3964 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3965 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3966 * @cfg {Boolean} specificTitle default false
3967 * @cfg {Array} buttons Array of buttons or standard button set..
3968 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3969 * @cfg {Boolean} animate default true
3970 * @cfg {Boolean} allow_close default true
3971 * @cfg {Boolean} fitwindow default false
3972 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3973 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3974 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3975 * @cfg {String} size (sm|lg|xl) default empty
3976 * @cfg {Number} max_width set the max width of modal
3977 * @cfg {Boolean} editableTitle can the title be edited
3982 * Create a new Modal Dialog
3983 * @param {Object} config The config object
3986 Roo.bootstrap.Modal = function(config){
3987 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3992 * The raw btnclick event for the button
3993 * @param {Roo.EventObject} e
3998 * Fire when dialog resize
3999 * @param {Roo.bootstrap.Modal} this
4000 * @param {Roo.EventObject} e
4004 * @event titlechanged
4005 * Fire when the editable title has been changed
4006 * @param {Roo.bootstrap.Modal} this
4007 * @param {Roo.EventObject} value
4009 "titlechanged" : true
4012 this.buttons = this.buttons || [];
4015 this.tmpl = Roo.factory(this.tmpl);
4020 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4022 title : 'test dialog',
4032 specificTitle: false,
4034 buttonPosition: 'right',
4056 editableTitle : false,
4058 onRender : function(ct, position)
4060 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4063 var cfg = Roo.apply({}, this.getAutoCreate());
4066 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4068 //if (!cfg.name.length) {
4072 cfg.cls += ' ' + this.cls;
4075 cfg.style = this.style;
4077 this.el = Roo.get(document.body).createChild(cfg, position);
4079 //var type = this.el.dom.type;
4082 if(this.tabIndex !== undefined){
4083 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4086 this.dialogEl = this.el.select('.modal-dialog',true).first();
4087 this.bodyEl = this.el.select('.modal-body',true).first();
4088 this.closeEl = this.el.select('.modal-header .close', true).first();
4089 this.headerEl = this.el.select('.modal-header',true).first();
4090 this.titleEl = this.el.select('.modal-title',true).first();
4091 this.footerEl = this.el.select('.modal-footer',true).first();
4093 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4095 //this.el.addClass("x-dlg-modal");
4097 if (this.buttons.length) {
4098 Roo.each(this.buttons, function(bb) {
4099 var b = Roo.apply({}, bb);
4100 b.xns = b.xns || Roo.bootstrap;
4101 b.xtype = b.xtype || 'Button';
4102 if (typeof(b.listeners) == 'undefined') {
4103 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4106 var btn = Roo.factory(b);
4108 btn.render(this.getButtonContainer());
4112 // render the children.
4115 if(typeof(this.items) != 'undefined'){
4116 var items = this.items;
4119 for(var i =0;i < items.length;i++) {
4120 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4124 this.items = nitems;
4126 // where are these used - they used to be body/close/footer
4130 //this.el.addClass([this.fieldClass, this.cls]);
4134 getAutoCreate : function()
4136 // we will default to modal-body-overflow - might need to remove or make optional later.
4138 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4139 html : this.html || ''
4144 cls : 'modal-title',
4148 if(this.specificTitle){ // WTF is this?
4153 if (this.allow_close && Roo.bootstrap.version == 3) {
4163 if (this.editableTitle) {
4165 cls: 'form-control roo-editable-title d-none',
4171 if (this.allow_close && Roo.bootstrap.version == 4) {
4181 if(this.size.length){
4182 size = 'modal-' + this.size;
4185 var footer = Roo.bootstrap.version == 3 ?
4187 cls : 'modal-footer',
4191 cls: 'btn-' + this.buttonPosition
4196 { // BS4 uses mr-auto on left buttons....
4197 cls : 'modal-footer'
4208 cls: "modal-dialog " + size,
4211 cls : "modal-content",
4214 cls : 'modal-header',
4229 modal.cls += ' fade';
4235 getChildContainer : function() {
4240 getButtonContainer : function() {
4242 return Roo.bootstrap.version == 4 ?
4243 this.el.select('.modal-footer',true).first()
4244 : this.el.select('.modal-footer div',true).first();
4247 initEvents : function()
4249 if (this.allow_close) {
4250 this.closeEl.on('click', this.hide, this);
4252 Roo.EventManager.onWindowResize(this.resize, this, true);
4253 if (this.editableTitle) {
4254 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4255 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4256 this.headerEditEl.on('keyup', function(e) {
4257 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4258 this.toggleHeaderInput(false)
4261 this.headerEditEl.on('blur', function(e) {
4262 this.toggleHeaderInput(false)
4271 this.maskEl.setSize(
4272 Roo.lib.Dom.getViewWidth(true),
4273 Roo.lib.Dom.getViewHeight(true)
4276 if (this.fitwindow) {
4278 this.dialogEl.setStyle( { 'max-width' : '100%' });
4280 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4281 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4286 if(this.max_width !== 0) {
4288 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4291 this.setSize(w, this.height);
4295 if(this.max_height) {
4296 this.setSize(w,Math.min(
4298 Roo.lib.Dom.getViewportHeight(true) - 60
4304 if(!this.fit_content) {
4305 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4309 this.setSize(w, Math.min(
4311 this.headerEl.getHeight() +
4312 this.footerEl.getHeight() +
4313 this.getChildHeight(this.bodyEl.dom.childNodes),
4314 Roo.lib.Dom.getViewportHeight(true) - 60)
4320 setSize : function(w,h)
4331 if (!this.rendered) {
4334 this.toggleHeaderInput(false);
4335 //this.el.setStyle('display', 'block');
4336 this.el.removeClass('hideing');
4337 this.el.dom.style.display='block';
4339 Roo.get(document.body).addClass('modal-open');
4341 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4344 this.el.addClass('show');
4345 this.el.addClass('in');
4348 this.el.addClass('show');
4349 this.el.addClass('in');
4352 // not sure how we can show data in here..
4354 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4357 Roo.get(document.body).addClass("x-body-masked");
4359 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4360 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4361 this.maskEl.dom.style.display = 'block';
4362 this.maskEl.addClass('show');
4367 this.fireEvent('show', this);
4369 // set zindex here - otherwise it appears to be ignored...
4370 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4373 this.items.forEach( function(e) {
4374 e.layout ? e.layout() : false;
4382 if(this.fireEvent("beforehide", this) !== false){
4384 this.maskEl.removeClass('show');
4386 this.maskEl.dom.style.display = '';
4387 Roo.get(document.body).removeClass("x-body-masked");
4388 this.el.removeClass('in');
4389 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4391 if(this.animate){ // why
4392 this.el.addClass('hideing');
4393 this.el.removeClass('show');
4395 if (!this.el.hasClass('hideing')) {
4396 return; // it's been shown again...
4399 this.el.dom.style.display='';
4401 Roo.get(document.body).removeClass('modal-open');
4402 this.el.removeClass('hideing');
4406 this.el.removeClass('show');
4407 this.el.dom.style.display='';
4408 Roo.get(document.body).removeClass('modal-open');
4411 this.fireEvent('hide', this);
4414 isVisible : function()
4417 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4421 addButton : function(str, cb)
4425 var b = Roo.apply({}, { html : str } );
4426 b.xns = b.xns || Roo.bootstrap;
4427 b.xtype = b.xtype || 'Button';
4428 if (typeof(b.listeners) == 'undefined') {
4429 b.listeners = { click : cb.createDelegate(this) };
4432 var btn = Roo.factory(b);
4434 btn.render(this.getButtonContainer());
4440 setDefaultButton : function(btn)
4442 //this.el.select('.modal-footer').()
4445 resizeTo: function(w,h)
4447 this.dialogEl.setWidth(w);
4449 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4451 this.bodyEl.setHeight(h - diff);
4453 this.fireEvent('resize', this);
4456 setContentSize : function(w, h)
4460 onButtonClick: function(btn,e)
4463 this.fireEvent('btnclick', btn.name, e);
4466 * Set the title of the Dialog
4467 * @param {String} str new Title
4469 setTitle: function(str) {
4470 this.titleEl.dom.innerHTML = str;
4474 * Set the body of the Dialog
4475 * @param {String} str new Title
4477 setBody: function(str) {
4478 this.bodyEl.dom.innerHTML = str;
4481 * Set the body of the Dialog using the template
4482 * @param {Obj} data - apply this data to the template and replace the body contents.
4484 applyBody: function(obj)
4487 Roo.log("Error - using apply Body without a template");
4490 this.tmpl.overwrite(this.bodyEl, obj);
4493 getChildHeight : function(child_nodes)
4497 child_nodes.length == 0
4502 var child_height = 0;
4504 for(var i = 0; i < child_nodes.length; i++) {
4507 * for modal with tabs...
4508 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4510 var layout_childs = child_nodes[i].childNodes;
4512 for(var j = 0; j < layout_childs.length; j++) {
4514 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4516 var layout_body_childs = layout_childs[j].childNodes;
4518 for(var k = 0; k < layout_body_childs.length; k++) {
4520 if(layout_body_childs[k].classList.contains('navbar')) {
4521 child_height += layout_body_childs[k].offsetHeight;
4525 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4527 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4529 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4531 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4532 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4547 child_height += child_nodes[i].offsetHeight;
4548 // Roo.log(child_nodes[i].offsetHeight);
4551 return child_height;
4553 toggleHeaderInput : function(is_edit)
4555 if (!this.editableTitle) {
4556 return; // not editable.
4558 if (is_edit && this.is_header_editing) {
4559 return; // already editing..
4563 this.headerEditEl.dom.value = this.title;
4564 this.headerEditEl.removeClass('d-none');
4565 this.headerEditEl.dom.focus();
4566 this.titleEl.addClass('d-none');
4568 this.is_header_editing = true;
4571 // flip back to not editing.
4572 this.title = this.headerEditEl.dom.value;
4573 this.headerEditEl.addClass('d-none');
4574 this.titleEl.removeClass('d-none');
4575 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4576 this.is_header_editing = false;
4577 this.fireEvent('titlechanged', this, this.title);
4586 Roo.apply(Roo.bootstrap.Modal, {
4588 * Button config that displays a single OK button
4597 * Button config that displays Yes and No buttons
4613 * Button config that displays OK and Cancel buttons
4628 * Button config that displays Yes, No and Cancel buttons
4653 * messagebox - can be used as a replace
4657 * @class Roo.MessageBox
4658 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4662 Roo.Msg.alert('Status', 'Changes saved successfully.');
4664 // Prompt for user data:
4665 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4667 // process text value...
4671 // Show a dialog using config options:
4673 title:'Save Changes?',
4674 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4675 buttons: Roo.Msg.YESNOCANCEL,
4682 Roo.bootstrap.MessageBox = function(){
4683 var dlg, opt, mask, waitTimer;
4684 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4685 var buttons, activeTextEl, bwidth;
4689 var handleButton = function(button){
4691 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4695 var handleHide = function(){
4697 dlg.el.removeClass(opt.cls);
4700 // Roo.TaskMgr.stop(waitTimer);
4701 // waitTimer = null;
4706 var updateButtons = function(b){
4709 buttons["ok"].hide();
4710 buttons["cancel"].hide();
4711 buttons["yes"].hide();
4712 buttons["no"].hide();
4713 dlg.footerEl.hide();
4717 dlg.footerEl.show();
4718 for(var k in buttons){
4719 if(typeof buttons[k] != "function"){
4722 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4723 width += buttons[k].el.getWidth()+15;
4733 var handleEsc = function(d, k, e){
4734 if(opt && opt.closable !== false){
4744 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4745 * @return {Roo.BasicDialog} The BasicDialog element
4747 getDialog : function(){
4749 dlg = new Roo.bootstrap.Modal( {
4752 //constraintoviewport:false,
4754 //collapsible : false,
4759 //buttonAlign:"center",
4760 closeClick : function(){
4761 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4764 handleButton("cancel");
4769 dlg.on("hide", handleHide);
4771 //dlg.addKeyListener(27, handleEsc);
4773 this.buttons = buttons;
4774 var bt = this.buttonText;
4775 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4776 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4777 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4778 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4780 bodyEl = dlg.bodyEl.createChild({
4782 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4783 '<textarea class="roo-mb-textarea"></textarea>' +
4784 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4786 msgEl = bodyEl.dom.firstChild;
4787 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4788 textboxEl.enableDisplayMode();
4789 textboxEl.addKeyListener([10,13], function(){
4790 if(dlg.isVisible() && opt && opt.buttons){
4793 }else if(opt.buttons.yes){
4794 handleButton("yes");
4798 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4799 textareaEl.enableDisplayMode();
4800 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4801 progressEl.enableDisplayMode();
4803 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4804 var pf = progressEl.dom.firstChild;
4806 pp = Roo.get(pf.firstChild);
4807 pp.setHeight(pf.offsetHeight);
4815 * Updates the message box body text
4816 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4817 * the XHTML-compliant non-breaking space character '&#160;')
4818 * @return {Roo.MessageBox} This message box
4820 updateText : function(text)
4822 if(!dlg.isVisible() && !opt.width){
4823 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4824 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4826 msgEl.innerHTML = text || ' ';
4828 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4829 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4831 Math.min(opt.width || cw , this.maxWidth),
4832 Math.max(opt.minWidth || this.minWidth, bwidth)
4835 activeTextEl.setWidth(w);
4837 if(dlg.isVisible()){
4838 dlg.fixedcenter = false;
4840 // to big, make it scroll. = But as usual stupid IE does not support
4843 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4844 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4845 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4847 bodyEl.dom.style.height = '';
4848 bodyEl.dom.style.overflowY = '';
4851 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4853 bodyEl.dom.style.overflowX = '';
4856 dlg.setContentSize(w, bodyEl.getHeight());
4857 if(dlg.isVisible()){
4858 dlg.fixedcenter = true;
4864 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4865 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4866 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4867 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4868 * @return {Roo.MessageBox} This message box
4870 updateProgress : function(value, text){
4872 this.updateText(text);
4875 if (pp) { // weird bug on my firefox - for some reason this is not defined
4876 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4877 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4883 * Returns true if the message box is currently displayed
4884 * @return {Boolean} True if the message box is visible, else false
4886 isVisible : function(){
4887 return dlg && dlg.isVisible();
4891 * Hides the message box if it is displayed
4894 if(this.isVisible()){
4900 * Displays a new message box, or reinitializes an existing message box, based on the config options
4901 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4902 * The following config object properties are supported:
4904 Property Type Description
4905 ---------- --------------- ------------------------------------------------------------------------------------
4906 animEl String/Element An id or Element from which the message box should animate as it opens and
4907 closes (defaults to undefined)
4908 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4909 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4910 closable Boolean False to hide the top-right close button (defaults to true). Note that
4911 progress and wait dialogs will ignore this property and always hide the
4912 close button as they can only be closed programmatically.
4913 cls String A custom CSS class to apply to the message box element
4914 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4915 displayed (defaults to 75)
4916 fn Function A callback function to execute after closing the dialog. The arguments to the
4917 function will be btn (the name of the button that was clicked, if applicable,
4918 e.g. "ok"), and text (the value of the active text field, if applicable).
4919 Progress and wait dialogs will ignore this option since they do not respond to
4920 user actions and can only be closed programmatically, so any required function
4921 should be called by the same code after it closes the dialog.
4922 icon String A CSS class that provides a background image to be used as an icon for
4923 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4924 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4925 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4926 modal Boolean False to allow user interaction with the page while the message box is
4927 displayed (defaults to true)
4928 msg String A string that will replace the existing message box body text (defaults
4929 to the XHTML-compliant non-breaking space character ' ')
4930 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4931 progress Boolean True to display a progress bar (defaults to false)
4932 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4933 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4934 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4935 title String The title text
4936 value String The string value to set into the active textbox element if displayed
4937 wait Boolean True to display a progress bar (defaults to false)
4938 width Number The width of the dialog in pixels
4945 msg: 'Please enter your address:',
4947 buttons: Roo.MessageBox.OKCANCEL,
4950 animEl: 'addAddressBtn'
4953 * @param {Object} config Configuration options
4954 * @return {Roo.MessageBox} This message box
4956 show : function(options)
4959 // this causes nightmares if you show one dialog after another
4960 // especially on callbacks..
4962 if(this.isVisible()){
4965 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4966 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4967 Roo.log("New Dialog Message:" + options.msg )
4968 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4969 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4972 var d = this.getDialog();
4974 d.setTitle(opt.title || " ");
4975 d.closeEl.setDisplayed(opt.closable !== false);
4976 activeTextEl = textboxEl;
4977 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4982 textareaEl.setHeight(typeof opt.multiline == "number" ?
4983 opt.multiline : this.defaultTextHeight);
4984 activeTextEl = textareaEl;
4993 progressEl.setDisplayed(opt.progress === true);
4995 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4997 this.updateProgress(0);
4998 activeTextEl.dom.value = opt.value || "";
5000 dlg.setDefaultButton(activeTextEl);
5002 var bs = opt.buttons;
5006 }else if(bs && bs.yes){
5007 db = buttons["yes"];
5009 dlg.setDefaultButton(db);
5011 bwidth = updateButtons(opt.buttons);
5012 this.updateText(opt.msg);
5014 d.el.addClass(opt.cls);
5016 d.proxyDrag = opt.proxyDrag === true;
5017 d.modal = opt.modal !== false;
5018 d.mask = opt.modal !== false ? mask : false;
5020 // force it to the end of the z-index stack so it gets a cursor in FF
5021 document.body.appendChild(dlg.el.dom);
5022 d.animateTarget = null;
5023 d.show(options.animEl);
5029 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5030 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5031 * and closing the message box when the process is complete.
5032 * @param {String} title The title bar text
5033 * @param {String} msg The message box body text
5034 * @return {Roo.MessageBox} This message box
5036 progress : function(title, msg){
5043 minWidth: this.minProgressWidth,
5050 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5051 * If a callback function is passed it will be called after the user clicks the button, and the
5052 * id of the button that was clicked will be passed as the only parameter to the callback
5053 * (could also be the top-right close button).
5054 * @param {String} title The title bar text
5055 * @param {String} msg The message box body text
5056 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5057 * @param {Object} scope (optional) The scope of the callback function
5058 * @return {Roo.MessageBox} This message box
5060 alert : function(title, msg, fn, scope)
5075 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5076 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5077 * You are responsible for closing the message box when the process is complete.
5078 * @param {String} msg The message box body text
5079 * @param {String} title (optional) The title bar text
5080 * @return {Roo.MessageBox} This message box
5082 wait : function(msg, title){
5093 waitTimer = Roo.TaskMgr.start({
5095 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5103 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5104 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5105 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5106 * @param {String} title The title bar text
5107 * @param {String} msg The message box body text
5108 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5109 * @param {Object} scope (optional) The scope of the callback function
5110 * @return {Roo.MessageBox} This message box
5112 confirm : function(title, msg, fn, scope){
5116 buttons: this.YESNO,
5125 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5126 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5127 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5128 * (could also be the top-right close button) and the text that was entered will be passed as the two
5129 * parameters to the callback.
5130 * @param {String} title The title bar text
5131 * @param {String} msg The message box body text
5132 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5133 * @param {Object} scope (optional) The scope of the callback function
5134 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5135 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5136 * @return {Roo.MessageBox} This message box
5138 prompt : function(title, msg, fn, scope, multiline){
5142 buttons: this.OKCANCEL,
5147 multiline: multiline,
5154 * Button config that displays a single OK button
5159 * Button config that displays Yes and No buttons
5162 YESNO : {yes:true, no:true},
5164 * Button config that displays OK and Cancel buttons
5167 OKCANCEL : {ok:true, cancel:true},
5169 * Button config that displays Yes, No and Cancel buttons
5172 YESNOCANCEL : {yes:true, no:true, cancel:true},
5175 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5178 defaultTextHeight : 75,
5180 * The maximum width in pixels of the message box (defaults to 600)
5185 * The minimum width in pixels of the message box (defaults to 100)
5190 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5191 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5194 minProgressWidth : 250,
5196 * An object containing the default button text strings that can be overriden for localized language support.
5197 * Supported properties are: ok, cancel, yes and no.
5198 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5211 * Shorthand for {@link Roo.MessageBox}
5213 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5214 Roo.Msg = Roo.Msg || Roo.MessageBox;
5223 * @class Roo.bootstrap.Navbar
5224 * @extends Roo.bootstrap.Component
5225 * Bootstrap Navbar class
5228 * Create a new Navbar
5229 * @param {Object} config The config object
5233 Roo.bootstrap.Navbar = function(config){
5234 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5238 * @event beforetoggle
5239 * Fire before toggle the menu
5240 * @param {Roo.EventObject} e
5242 "beforetoggle" : true
5246 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5255 getAutoCreate : function(){
5258 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5262 initEvents :function ()
5264 //Roo.log(this.el.select('.navbar-toggle',true));
5265 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5272 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5274 var size = this.el.getSize();
5275 this.maskEl.setSize(size.width, size.height);
5276 this.maskEl.enableDisplayMode("block");
5285 getChildContainer : function()
5287 if (this.el && this.el.select('.collapse').getCount()) {
5288 return this.el.select('.collapse',true).first();
5303 onToggle : function()
5306 if(this.fireEvent('beforetoggle', this) === false){
5309 var ce = this.el.select('.navbar-collapse',true).first();
5311 if (!ce.hasClass('show')) {
5321 * Expand the navbar pulldown
5323 expand : function ()
5326 var ce = this.el.select('.navbar-collapse',true).first();
5327 if (ce.hasClass('collapsing')) {
5330 ce.dom.style.height = '';
5332 ce.addClass('in'); // old...
5333 ce.removeClass('collapse');
5334 ce.addClass('show');
5335 var h = ce.getHeight();
5337 ce.removeClass('show');
5338 // at this point we should be able to see it..
5339 ce.addClass('collapsing');
5341 ce.setHeight(0); // resize it ...
5342 ce.on('transitionend', function() {
5343 //Roo.log('done transition');
5344 ce.removeClass('collapsing');
5345 ce.addClass('show');
5346 ce.removeClass('collapse');
5348 ce.dom.style.height = '';
5349 }, this, { single: true} );
5351 ce.dom.scrollTop = 0;
5354 * Collapse the navbar pulldown
5356 collapse : function()
5358 var ce = this.el.select('.navbar-collapse',true).first();
5360 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5361 // it's collapsed or collapsing..
5364 ce.removeClass('in'); // old...
5365 ce.setHeight(ce.getHeight());
5366 ce.removeClass('show');
5367 ce.addClass('collapsing');
5369 ce.on('transitionend', function() {
5370 ce.dom.style.height = '';
5371 ce.removeClass('collapsing');
5372 ce.addClass('collapse');
5373 }, this, { single: true} );
5393 * @class Roo.bootstrap.NavSimplebar
5394 * @extends Roo.bootstrap.Navbar
5395 * Bootstrap Sidebar class
5397 * @cfg {Boolean} inverse is inverted color
5399 * @cfg {String} type (nav | pills | tabs)
5400 * @cfg {Boolean} arrangement stacked | justified
5401 * @cfg {String} align (left | right) alignment
5403 * @cfg {Boolean} main (true|false) main nav bar? default false
5404 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5406 * @cfg {String} tag (header|footer|nav|div) default is nav
5408 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5412 * Create a new Sidebar
5413 * @param {Object} config The config object
5417 Roo.bootstrap.NavSimplebar = function(config){
5418 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5421 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5437 getAutoCreate : function(){
5441 tag : this.tag || 'div',
5442 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5444 if (['light','white'].indexOf(this.weight) > -1) {
5445 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5447 cfg.cls += ' bg-' + this.weight;
5450 cfg.cls += ' navbar-inverse';
5454 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5456 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5465 cls: 'nav nav-' + this.xtype,
5471 this.type = this.type || 'nav';
5472 if (['tabs','pills'].indexOf(this.type) != -1) {
5473 cfg.cn[0].cls += ' nav-' + this.type
5477 if (this.type!=='nav') {
5478 Roo.log('nav type must be nav/tabs/pills')
5480 cfg.cn[0].cls += ' navbar-nav'
5486 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5487 cfg.cn[0].cls += ' nav-' + this.arrangement;
5491 if (this.align === 'right') {
5492 cfg.cn[0].cls += ' navbar-right';
5517 * navbar-expand-md fixed-top
5521 * @class Roo.bootstrap.NavHeaderbar
5522 * @extends Roo.bootstrap.NavSimplebar
5523 * Bootstrap Sidebar class
5525 * @cfg {String} brand what is brand
5526 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5527 * @cfg {String} brand_href href of the brand
5528 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5529 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5530 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5531 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5534 * Create a new Sidebar
5535 * @param {Object} config The config object
5539 Roo.bootstrap.NavHeaderbar = function(config){
5540 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5544 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5551 desktopCenter : false,
5554 getAutoCreate : function(){
5557 tag: this.nav || 'nav',
5558 cls: 'navbar navbar-expand-md',
5564 if (this.desktopCenter) {
5565 cn.push({cls : 'container', cn : []});
5573 cls: 'navbar-toggle navbar-toggler',
5574 'data-toggle': 'collapse',
5579 html: 'Toggle navigation'
5583 cls: 'icon-bar navbar-toggler-icon'
5596 cn.push( Roo.bootstrap.version == 4 ? btn : {
5598 cls: 'navbar-header',
5607 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5611 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5613 if (['light','white'].indexOf(this.weight) > -1) {
5614 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5616 cfg.cls += ' bg-' + this.weight;
5619 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5620 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5622 // tag can override this..
5624 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5627 if (this.brand !== '') {
5628 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5629 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5631 href: this.brand_href ? this.brand_href : '#',
5632 cls: 'navbar-brand',
5640 cfg.cls += ' main-nav';
5648 getHeaderChildContainer : function()
5650 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5651 return this.el.select('.navbar-header',true).first();
5654 return this.getChildContainer();
5657 getChildContainer : function()
5660 return this.el.select('.roo-navbar-collapse',true).first();
5665 initEvents : function()
5667 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5669 if (this.autohide) {
5674 Roo.get(document).on('scroll',function(e) {
5675 var ns = Roo.get(document).getScroll().top;
5676 var os = prevScroll;
5680 ft.removeClass('slideDown');
5681 ft.addClass('slideUp');
5684 ft.removeClass('slideUp');
5685 ft.addClass('slideDown');
5706 * @class Roo.bootstrap.NavSidebar
5707 * @extends Roo.bootstrap.Navbar
5708 * Bootstrap Sidebar class
5711 * Create a new Sidebar
5712 * @param {Object} config The config object
5716 Roo.bootstrap.NavSidebar = function(config){
5717 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5720 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5722 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5724 getAutoCreate : function(){
5729 cls: 'sidebar sidebar-nav'
5751 * @class Roo.bootstrap.NavGroup
5752 * @extends Roo.bootstrap.Component
5753 * Bootstrap NavGroup class
5754 * @cfg {String} align (left|right)
5755 * @cfg {Boolean} inverse
5756 * @cfg {String} type (nav|pills|tab) default nav
5757 * @cfg {String} navId - reference Id for navbar.
5758 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5761 * Create a new nav group
5762 * @param {Object} config The config object
5765 Roo.bootstrap.NavGroup = function(config){
5766 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5769 Roo.bootstrap.NavGroup.register(this);
5773 * Fires when the active item changes
5774 * @param {Roo.bootstrap.NavGroup} this
5775 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5776 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5783 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5795 getAutoCreate : function()
5797 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5803 if (Roo.bootstrap.version == 4) {
5804 if (['tabs','pills'].indexOf(this.type) != -1) {
5805 cfg.cls += ' nav-' + this.type;
5807 // trying to remove so header bar can right align top?
5808 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5809 // do not use on header bar...
5810 cfg.cls += ' navbar-nav';
5815 if (['tabs','pills'].indexOf(this.type) != -1) {
5816 cfg.cls += ' nav-' + this.type
5818 if (this.type !== 'nav') {
5819 Roo.log('nav type must be nav/tabs/pills')
5821 cfg.cls += ' navbar-nav'
5825 if (this.parent() && this.parent().sidebar) {
5828 cls: 'dashboard-menu sidebar-menu'
5834 if (this.form === true) {
5837 cls: 'navbar-form form-inline'
5839 //nav navbar-right ml-md-auto
5840 if (this.align === 'right') {
5841 cfg.cls += ' navbar-right ml-md-auto';
5843 cfg.cls += ' navbar-left';
5847 if (this.align === 'right') {
5848 cfg.cls += ' navbar-right ml-md-auto';
5850 cfg.cls += ' mr-auto';
5854 cfg.cls += ' navbar-inverse';
5862 * sets the active Navigation item
5863 * @param {Roo.bootstrap.NavItem} the new current navitem
5865 setActiveItem : function(item)
5868 Roo.each(this.navItems, function(v){
5873 v.setActive(false, true);
5880 item.setActive(true, true);
5881 this.fireEvent('changed', this, item, prev);
5886 * gets the active Navigation item
5887 * @return {Roo.bootstrap.NavItem} the current navitem
5889 getActive : function()
5893 Roo.each(this.navItems, function(v){
5904 indexOfNav : function()
5908 Roo.each(this.navItems, function(v,i){
5919 * adds a Navigation item
5920 * @param {Roo.bootstrap.NavItem} the navitem to add
5922 addItem : function(cfg)
5924 if (this.form && Roo.bootstrap.version == 4) {
5927 var cn = new Roo.bootstrap.NavItem(cfg);
5929 cn.parentId = this.id;
5930 cn.onRender(this.el, null);
5934 * register a Navigation item
5935 * @param {Roo.bootstrap.NavItem} the navitem to add
5937 register : function(item)
5939 this.navItems.push( item);
5940 item.navId = this.navId;
5945 * clear all the Navigation item
5948 clearAll : function()
5951 this.el.dom.innerHTML = '';
5954 getNavItem: function(tabId)
5957 Roo.each(this.navItems, function(e) {
5958 if (e.tabId == tabId) {
5968 setActiveNext : function()
5970 var i = this.indexOfNav(this.getActive());
5971 if (i > this.navItems.length) {
5974 this.setActiveItem(this.navItems[i+1]);
5976 setActivePrev : function()
5978 var i = this.indexOfNav(this.getActive());
5982 this.setActiveItem(this.navItems[i-1]);
5984 clearWasActive : function(except) {
5985 Roo.each(this.navItems, function(e) {
5986 if (e.tabId != except.tabId && e.was_active) {
5987 e.was_active = false;
5994 getWasActive : function ()
5997 Roo.each(this.navItems, function(e) {
6012 Roo.apply(Roo.bootstrap.NavGroup, {
6016 * register a Navigation Group
6017 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6019 register : function(navgrp)
6021 this.groups[navgrp.navId] = navgrp;
6025 * fetch a Navigation Group based on the navigation ID
6026 * @param {string} the navgroup to add
6027 * @returns {Roo.bootstrap.NavGroup} the navgroup
6029 get: function(navId) {
6030 if (typeof(this.groups[navId]) == 'undefined') {
6032 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6034 return this.groups[navId] ;
6049 * @class Roo.bootstrap.NavItem
6050 * @extends Roo.bootstrap.Component
6051 * Bootstrap Navbar.NavItem class
6052 * @cfg {String} href link to
6053 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6054 * @cfg {Boolean} button_outline show and outlined button
6055 * @cfg {String} html content of button
6056 * @cfg {String} badge text inside badge
6057 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6058 * @cfg {String} glyphicon DEPRICATED - use fa
6059 * @cfg {String} icon DEPRICATED - use fa
6060 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6061 * @cfg {Boolean} active Is item active
6062 * @cfg {Boolean} disabled Is item disabled
6063 * @cfg {String} linkcls Link Class
6064 * @cfg {Boolean} preventDefault (true | false) default false
6065 * @cfg {String} tabId the tab that this item activates.
6066 * @cfg {String} tagtype (a|span) render as a href or span?
6067 * @cfg {Boolean} animateRef (true|false) link to element default false
6070 * Create a new Navbar Item
6071 * @param {Object} config The config object
6073 Roo.bootstrap.NavItem = function(config){
6074 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6079 * The raw click event for the entire grid.
6080 * @param {Roo.EventObject} e
6085 * Fires when the active item active state changes
6086 * @param {Roo.bootstrap.NavItem} this
6087 * @param {boolean} state the new state
6093 * Fires when scroll to element
6094 * @param {Roo.bootstrap.NavItem} this
6095 * @param {Object} options
6096 * @param {Roo.EventObject} e
6104 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6113 preventDefault : false,
6121 button_outline : false,
6125 getAutoCreate : function(){
6132 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6135 cfg.cls += ' active' ;
6137 if (this.disabled) {
6138 cfg.cls += ' disabled';
6142 if (this.button_weight.length) {
6143 cfg.tag = this.href ? 'a' : 'button';
6144 cfg.html = this.html || '';
6145 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6147 cfg.href = this.href;
6150 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6153 // menu .. should add dropdown-menu class - so no need for carat..
6155 if (this.badge !== '') {
6157 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6162 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6166 href : this.href || "#",
6167 html: this.html || ''
6170 if (this.tagtype == 'a') {
6171 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6175 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6178 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6180 if(this.glyphicon) {
6181 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6186 cfg.cn[0].html += " <span class='caret'></span>";
6190 if (this.badge !== '') {
6192 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6200 onRender : function(ct, position)
6202 // Roo.log("Call onRender: " + this.xtype);
6203 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6207 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6208 this.navLink = this.el.select('.nav-link',true).first();
6213 initEvents: function()
6215 if (typeof (this.menu) != 'undefined') {
6216 this.menu.parentType = this.xtype;
6217 this.menu.triggerEl = this.el;
6218 this.menu = this.addxtype(Roo.apply({}, this.menu));
6221 this.el.on('click', this.onClick, this);
6223 //if(this.tagtype == 'span'){
6224 // this.el.select('span',true).on('click', this.onClick, this);
6227 // at this point parent should be available..
6228 this.parent().register(this);
6231 onClick : function(e)
6233 if (e.getTarget('.dropdown-menu-item')) {
6234 // did you click on a menu itemm.... - then don't trigger onclick..
6239 this.preventDefault ||
6242 Roo.log("NavItem - prevent Default?");
6246 if (this.disabled) {
6250 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6251 if (tg && tg.transition) {
6252 Roo.log("waiting for the transitionend");
6258 //Roo.log("fire event clicked");
6259 if(this.fireEvent('click', this, e) === false){
6263 if(this.tagtype == 'span'){
6267 //Roo.log(this.href);
6268 var ael = this.el.select('a',true).first();
6271 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6272 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6273 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6274 return; // ignore... - it's a 'hash' to another page.
6276 Roo.log("NavItem - prevent Default?");
6278 this.scrollToElement(e);
6282 var p = this.parent();
6284 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6285 if (typeof(p.setActiveItem) !== 'undefined') {
6286 p.setActiveItem(this);
6290 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6291 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6292 // remove the collapsed menu expand...
6293 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6297 isActive: function () {
6300 setActive : function(state, fire, is_was_active)
6302 if (this.active && !state && this.navId) {
6303 this.was_active = true;
6304 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6306 nv.clearWasActive(this);
6310 this.active = state;
6313 this.el.removeClass('active');
6314 this.navLink ? this.navLink.removeClass('active') : false;
6315 } else if (!this.el.hasClass('active')) {
6317 this.el.addClass('active');
6318 if (Roo.bootstrap.version == 4 && this.navLink ) {
6319 this.navLink.addClass('active');
6324 this.fireEvent('changed', this, state);
6327 // show a panel if it's registered and related..
6329 if (!this.navId || !this.tabId || !state || is_was_active) {
6333 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6337 var pan = tg.getPanelByName(this.tabId);
6341 // if we can not flip to new panel - go back to old nav highlight..
6342 if (false == tg.showPanel(pan)) {
6343 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6345 var onav = nv.getWasActive();
6347 onav.setActive(true, false, true);
6356 // this should not be here...
6357 setDisabled : function(state)
6359 this.disabled = state;
6361 this.el.removeClass('disabled');
6362 } else if (!this.el.hasClass('disabled')) {
6363 this.el.addClass('disabled');
6369 * Fetch the element to display the tooltip on.
6370 * @return {Roo.Element} defaults to this.el
6372 tooltipEl : function()
6374 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6377 scrollToElement : function(e)
6379 var c = document.body;
6382 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6384 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6385 c = document.documentElement;
6388 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6394 var o = target.calcOffsetsTo(c);
6401 this.fireEvent('scrollto', this, options, e);
6403 Roo.get(c).scrollTo('top', options.value, true);
6416 * <span> icon </span>
6417 * <span> text </span>
6418 * <span>badge </span>
6422 * @class Roo.bootstrap.NavSidebarItem
6423 * @extends Roo.bootstrap.NavItem
6424 * Bootstrap Navbar.NavSidebarItem class
6425 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6426 * {Boolean} open is the menu open
6427 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6428 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6429 * {String} buttonSize (sm|md|lg)the extra classes for the button
6430 * {Boolean} showArrow show arrow next to the text (default true)
6432 * Create a new Navbar Button
6433 * @param {Object} config The config object
6435 Roo.bootstrap.NavSidebarItem = function(config){
6436 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6441 * The raw click event for the entire grid.
6442 * @param {Roo.EventObject} e
6447 * Fires when the active item active state changes
6448 * @param {Roo.bootstrap.NavSidebarItem} this
6449 * @param {boolean} state the new state
6457 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6459 badgeWeight : 'default',
6465 buttonWeight : 'default',
6471 getAutoCreate : function(){
6476 href : this.href || '#',
6482 if(this.buttonView){
6485 href : this.href || '#',
6486 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6499 cfg.cls += ' active';
6502 if (this.disabled) {
6503 cfg.cls += ' disabled';
6506 cfg.cls += ' open x-open';
6509 if (this.glyphicon || this.icon) {
6510 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6511 a.cn.push({ tag : 'i', cls : c }) ;
6514 if(!this.buttonView){
6517 html : this.html || ''
6524 if (this.badge !== '') {
6525 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6531 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6534 a.cls += ' dropdown-toggle treeview' ;
6540 initEvents : function()
6542 if (typeof (this.menu) != 'undefined') {
6543 this.menu.parentType = this.xtype;
6544 this.menu.triggerEl = this.el;
6545 this.menu = this.addxtype(Roo.apply({}, this.menu));
6548 this.el.on('click', this.onClick, this);
6550 if(this.badge !== ''){
6551 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6556 onClick : function(e)
6563 if(this.preventDefault){
6567 this.fireEvent('click', this, e);
6570 disable : function()
6572 this.setDisabled(true);
6577 this.setDisabled(false);
6580 setDisabled : function(state)
6582 if(this.disabled == state){
6586 this.disabled = state;
6589 this.el.addClass('disabled');
6593 this.el.removeClass('disabled');
6598 setActive : function(state)
6600 if(this.active == state){
6604 this.active = state;
6607 this.el.addClass('active');
6611 this.el.removeClass('active');
6616 isActive: function ()
6621 setBadge : function(str)
6627 this.badgeEl.dom.innerHTML = str;
6642 Roo.namespace('Roo.bootstrap.breadcrumb');
6646 * @class Roo.bootstrap.breadcrumb.Nav
6647 * @extends Roo.bootstrap.Component
6648 * Bootstrap Breadcrumb Nav Class
6650 * @children Roo.bootstrap.breadcrumb.Item
6653 * Create a new breadcrumb.Nav
6654 * @param {Object} config The config object
6658 Roo.bootstrap.breadcrumb.Nav = function(config){
6659 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6664 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6666 getAutoCreate : function()
6683 initEvents: function()
6685 this.olEl = this.el.select('ol',true).first();
6687 getChildContainer : function()
6703 * @class Roo.bootstrap.breadcrumb.Nav
6704 * @extends Roo.bootstrap.Component
6705 * Bootstrap Breadcrumb Nav Class
6707 * @children Roo.bootstrap.breadcrumb.Component
6708 * @cfg {String} html the content of the link.
6709 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6710 * @cfg {Boolean} active is it active
6714 * Create a new breadcrumb.Nav
6715 * @param {Object} config The config object
6718 Roo.bootstrap.breadcrumb.Item = function(config){
6719 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6724 * The img click event for the img.
6725 * @param {Roo.EventObject} e
6732 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6737 getAutoCreate : function()
6742 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6744 if (this.href !== false) {
6751 cfg.html = this.html;
6757 initEvents: function()
6760 this.el.select('a', true).first().on('click',this.onClick, this)
6764 onClick : function(e)
6767 this.fireEvent('click',this, e);
6780 * @class Roo.bootstrap.Row
6781 * @extends Roo.bootstrap.Component
6782 * Bootstrap Row class (contains columns...)
6786 * @param {Object} config The config object
6789 Roo.bootstrap.Row = function(config){
6790 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6793 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6795 getAutoCreate : function(){
6814 * @class Roo.bootstrap.Pagination
6815 * @extends Roo.bootstrap.Component
6816 * Bootstrap Pagination class
6817 * @cfg {String} size xs | sm | md | lg
6818 * @cfg {Boolean} inverse false | true
6821 * Create a new Pagination
6822 * @param {Object} config The config object
6825 Roo.bootstrap.Pagination = function(config){
6826 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6829 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6835 getAutoCreate : function(){
6841 cfg.cls += ' inverse';
6847 cfg.cls += " " + this.cls;
6865 * @class Roo.bootstrap.PaginationItem
6866 * @extends Roo.bootstrap.Component
6867 * Bootstrap PaginationItem class
6868 * @cfg {String} html text
6869 * @cfg {String} href the link
6870 * @cfg {Boolean} preventDefault (true | false) default true
6871 * @cfg {Boolean} active (true | false) default false
6872 * @cfg {Boolean} disabled default false
6876 * Create a new PaginationItem
6877 * @param {Object} config The config object
6881 Roo.bootstrap.PaginationItem = function(config){
6882 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6887 * The raw click event for the entire grid.
6888 * @param {Roo.EventObject} e
6894 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6898 preventDefault: true,
6903 getAutoCreate : function(){
6909 href : this.href ? this.href : '#',
6910 html : this.html ? this.html : ''
6920 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6924 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6930 initEvents: function() {
6932 this.el.on('click', this.onClick, this);
6935 onClick : function(e)
6937 Roo.log('PaginationItem on click ');
6938 if(this.preventDefault){
6946 this.fireEvent('click', this, e);
6962 * @class Roo.bootstrap.Slider
6963 * @extends Roo.bootstrap.Component
6964 * Bootstrap Slider class
6967 * Create a new Slider
6968 * @param {Object} config The config object
6971 Roo.bootstrap.Slider = function(config){
6972 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6975 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6977 getAutoCreate : function(){
6981 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6985 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6997 * Ext JS Library 1.1.1
6998 * Copyright(c) 2006-2007, Ext JS, LLC.
7000 * Originally Released Under LGPL - original licence link has changed is not relivant.
7003 * <script type="text/javascript">
7008 * @class Roo.grid.ColumnModel
7009 * @extends Roo.util.Observable
7010 * This is the default implementation of a ColumnModel used by the Grid. It defines
7011 * the columns in the grid.
7014 var colModel = new Roo.grid.ColumnModel([
7015 {header: "Ticker", width: 60, sortable: true, locked: true},
7016 {header: "Company Name", width: 150, sortable: true},
7017 {header: "Market Cap.", width: 100, sortable: true},
7018 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7019 {header: "Employees", width: 100, sortable: true, resizable: false}
7024 * The config options listed for this class are options which may appear in each
7025 * individual column definition.
7026 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7028 * @param {Object} config An Array of column config objects. See this class's
7029 * config objects for details.
7031 Roo.grid.ColumnModel = function(config){
7033 * The config passed into the constructor
7035 this.config = config;
7038 // if no id, create one
7039 // if the column does not have a dataIndex mapping,
7040 // map it to the order it is in the config
7041 for(var i = 0, len = config.length; i < len; i++){
7043 if(typeof c.dataIndex == "undefined"){
7046 if(typeof c.renderer == "string"){
7047 c.renderer = Roo.util.Format[c.renderer];
7049 if(typeof c.id == "undefined"){
7052 if(c.editor && c.editor.xtype){
7053 c.editor = Roo.factory(c.editor, Roo.grid);
7055 if(c.editor && c.editor.isFormField){
7056 c.editor = new Roo.grid.GridEditor(c.editor);
7058 this.lookup[c.id] = c;
7062 * The width of columns which have no width specified (defaults to 100)
7065 this.defaultWidth = 100;
7068 * Default sortable of columns which have no sortable specified (defaults to false)
7071 this.defaultSortable = false;
7075 * @event widthchange
7076 * Fires when the width of a column changes.
7077 * @param {ColumnModel} this
7078 * @param {Number} columnIndex The column index
7079 * @param {Number} newWidth The new width
7081 "widthchange": true,
7083 * @event headerchange
7084 * Fires when the text of a header changes.
7085 * @param {ColumnModel} this
7086 * @param {Number} columnIndex The column index
7087 * @param {Number} newText The new header text
7089 "headerchange": true,
7091 * @event hiddenchange
7092 * Fires when a column is hidden or "unhidden".
7093 * @param {ColumnModel} this
7094 * @param {Number} columnIndex The column index
7095 * @param {Boolean} hidden true if hidden, false otherwise
7097 "hiddenchange": true,
7099 * @event columnmoved
7100 * Fires when a column is moved.
7101 * @param {ColumnModel} this
7102 * @param {Number} oldIndex
7103 * @param {Number} newIndex
7105 "columnmoved" : true,
7107 * @event columlockchange
7108 * Fires when a column's locked state is changed
7109 * @param {ColumnModel} this
7110 * @param {Number} colIndex
7111 * @param {Boolean} locked true if locked
7113 "columnlockchange" : true
7115 Roo.grid.ColumnModel.superclass.constructor.call(this);
7117 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7119 * @cfg {String} header The header text to display in the Grid view.
7122 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7123 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7124 * specified, the column's index is used as an index into the Record's data Array.
7127 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7128 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7131 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7132 * Defaults to the value of the {@link #defaultSortable} property.
7133 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7136 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7139 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7142 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7145 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7148 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7149 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7150 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7151 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7154 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7157 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7160 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7163 * @cfg {String} cursor (Optional)
7166 * @cfg {String} tooltip (Optional)
7169 * @cfg {Number} xs (Optional)
7172 * @cfg {Number} sm (Optional)
7175 * @cfg {Number} md (Optional)
7178 * @cfg {Number} lg (Optional)
7181 * Returns the id of the column at the specified index.
7182 * @param {Number} index The column index
7183 * @return {String} the id
7185 getColumnId : function(index){
7186 return this.config[index].id;
7190 * Returns the column for a specified id.
7191 * @param {String} id The column id
7192 * @return {Object} the column
7194 getColumnById : function(id){
7195 return this.lookup[id];
7200 * Returns the column for a specified dataIndex.
7201 * @param {String} dataIndex The column dataIndex
7202 * @return {Object|Boolean} the column or false if not found
7204 getColumnByDataIndex: function(dataIndex){
7205 var index = this.findColumnIndex(dataIndex);
7206 return index > -1 ? this.config[index] : false;
7210 * Returns the index for a specified column id.
7211 * @param {String} id The column id
7212 * @return {Number} the index, or -1 if not found
7214 getIndexById : function(id){
7215 for(var i = 0, len = this.config.length; i < len; i++){
7216 if(this.config[i].id == id){
7224 * Returns the index for a specified column dataIndex.
7225 * @param {String} dataIndex The column dataIndex
7226 * @return {Number} the index, or -1 if not found
7229 findColumnIndex : function(dataIndex){
7230 for(var i = 0, len = this.config.length; i < len; i++){
7231 if(this.config[i].dataIndex == dataIndex){
7239 moveColumn : function(oldIndex, newIndex){
7240 var c = this.config[oldIndex];
7241 this.config.splice(oldIndex, 1);
7242 this.config.splice(newIndex, 0, c);
7243 this.dataMap = null;
7244 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7247 isLocked : function(colIndex){
7248 return this.config[colIndex].locked === true;
7251 setLocked : function(colIndex, value, suppressEvent){
7252 if(this.isLocked(colIndex) == value){
7255 this.config[colIndex].locked = value;
7257 this.fireEvent("columnlockchange", this, colIndex, value);
7261 getTotalLockedWidth : function(){
7263 for(var i = 0; i < this.config.length; i++){
7264 if(this.isLocked(i) && !this.isHidden(i)){
7265 this.totalWidth += this.getColumnWidth(i);
7271 getLockedCount : function(){
7272 for(var i = 0, len = this.config.length; i < len; i++){
7273 if(!this.isLocked(i)){
7278 return this.config.length;
7282 * Returns the number of columns.
7285 getColumnCount : function(visibleOnly){
7286 if(visibleOnly === true){
7288 for(var i = 0, len = this.config.length; i < len; i++){
7289 if(!this.isHidden(i)){
7295 return this.config.length;
7299 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7300 * @param {Function} fn
7301 * @param {Object} scope (optional)
7302 * @return {Array} result
7304 getColumnsBy : function(fn, scope){
7306 for(var i = 0, len = this.config.length; i < len; i++){
7307 var c = this.config[i];
7308 if(fn.call(scope||this, c, i) === true){
7316 * Returns true if the specified column is sortable.
7317 * @param {Number} col The column index
7320 isSortable : function(col){
7321 if(typeof this.config[col].sortable == "undefined"){
7322 return this.defaultSortable;
7324 return this.config[col].sortable;
7328 * Returns the rendering (formatting) function defined for the column.
7329 * @param {Number} col The column index.
7330 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7332 getRenderer : function(col){
7333 if(!this.config[col].renderer){
7334 return Roo.grid.ColumnModel.defaultRenderer;
7336 return this.config[col].renderer;
7340 * Sets the rendering (formatting) function for a column.
7341 * @param {Number} col The column index
7342 * @param {Function} fn The function to use to process the cell's raw data
7343 * to return HTML markup for the grid view. The render function is called with
7344 * the following parameters:<ul>
7345 * <li>Data value.</li>
7346 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7347 * <li>css A CSS style string to apply to the table cell.</li>
7348 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7349 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7350 * <li>Row index</li>
7351 * <li>Column index</li>
7352 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7354 setRenderer : function(col, fn){
7355 this.config[col].renderer = fn;
7359 * Returns the width for the specified column.
7360 * @param {Number} col The column index
7363 getColumnWidth : function(col){
7364 return this.config[col].width * 1 || this.defaultWidth;
7368 * Sets the width for a column.
7369 * @param {Number} col The column index
7370 * @param {Number} width The new width
7372 setColumnWidth : function(col, width, suppressEvent){
7373 this.config[col].width = width;
7374 this.totalWidth = null;
7376 this.fireEvent("widthchange", this, col, width);
7381 * Returns the total width of all columns.
7382 * @param {Boolean} includeHidden True to include hidden column widths
7385 getTotalWidth : function(includeHidden){
7386 if(!this.totalWidth){
7387 this.totalWidth = 0;
7388 for(var i = 0, len = this.config.length; i < len; i++){
7389 if(includeHidden || !this.isHidden(i)){
7390 this.totalWidth += this.getColumnWidth(i);
7394 return this.totalWidth;
7398 * Returns the header for the specified column.
7399 * @param {Number} col The column index
7402 getColumnHeader : function(col){
7403 return this.config[col].header;
7407 * Sets the header for a column.
7408 * @param {Number} col The column index
7409 * @param {String} header The new header
7411 setColumnHeader : function(col, header){
7412 this.config[col].header = header;
7413 this.fireEvent("headerchange", this, col, header);
7417 * Returns the tooltip for the specified column.
7418 * @param {Number} col The column index
7421 getColumnTooltip : function(col){
7422 return this.config[col].tooltip;
7425 * Sets the tooltip for a column.
7426 * @param {Number} col The column index
7427 * @param {String} tooltip The new tooltip
7429 setColumnTooltip : function(col, tooltip){
7430 this.config[col].tooltip = tooltip;
7434 * Returns the dataIndex for the specified column.
7435 * @param {Number} col The column index
7438 getDataIndex : function(col){
7439 return this.config[col].dataIndex;
7443 * Sets the dataIndex for a column.
7444 * @param {Number} col The column index
7445 * @param {Number} dataIndex The new dataIndex
7447 setDataIndex : function(col, dataIndex){
7448 this.config[col].dataIndex = dataIndex;
7454 * Returns true if the cell is editable.
7455 * @param {Number} colIndex The column index
7456 * @param {Number} rowIndex The row index - this is nto actually used..?
7459 isCellEditable : function(colIndex, rowIndex){
7460 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7464 * Returns the editor defined for the cell/column.
7465 * return false or null to disable editing.
7466 * @param {Number} colIndex The column index
7467 * @param {Number} rowIndex The row index
7470 getCellEditor : function(colIndex, rowIndex){
7471 return this.config[colIndex].editor;
7475 * Sets if a column is editable.
7476 * @param {Number} col The column index
7477 * @param {Boolean} editable True if the column is editable
7479 setEditable : function(col, editable){
7480 this.config[col].editable = editable;
7485 * Returns true if the column is hidden.
7486 * @param {Number} colIndex The column index
7489 isHidden : function(colIndex){
7490 return this.config[colIndex].hidden;
7495 * Returns true if the column width cannot be changed
7497 isFixed : function(colIndex){
7498 return this.config[colIndex].fixed;
7502 * Returns true if the column can be resized
7505 isResizable : function(colIndex){
7506 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7509 * Sets if a column is hidden.
7510 * @param {Number} colIndex The column index
7511 * @param {Boolean} hidden True if the column is hidden
7513 setHidden : function(colIndex, hidden){
7514 this.config[colIndex].hidden = hidden;
7515 this.totalWidth = null;
7516 this.fireEvent("hiddenchange", this, colIndex, hidden);
7520 * Sets the editor for a column.
7521 * @param {Number} col The column index
7522 * @param {Object} editor The editor object
7524 setEditor : function(col, editor){
7525 this.config[col].editor = editor;
7529 Roo.grid.ColumnModel.defaultRenderer = function(value)
7531 if(typeof value == "object") {
7534 if(typeof value == "string" && value.length < 1){
7538 return String.format("{0}", value);
7541 // Alias for backwards compatibility
7542 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7545 * Ext JS Library 1.1.1
7546 * Copyright(c) 2006-2007, Ext JS, LLC.
7548 * Originally Released Under LGPL - original licence link has changed is not relivant.
7551 * <script type="text/javascript">
7555 * @class Roo.LoadMask
7556 * A simple utility class for generically masking elements while loading data. If the element being masked has
7557 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7558 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7559 * element's UpdateManager load indicator and will be destroyed after the initial load.
7561 * Create a new LoadMask
7562 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7563 * @param {Object} config The config object
7565 Roo.LoadMask = function(el, config){
7566 this.el = Roo.get(el);
7567 Roo.apply(this, config);
7569 this.store.on('beforeload', this.onBeforeLoad, this);
7570 this.store.on('load', this.onLoad, this);
7571 this.store.on('loadexception', this.onLoadException, this);
7572 this.removeMask = false;
7574 var um = this.el.getUpdateManager();
7575 um.showLoadIndicator = false; // disable the default indicator
7576 um.on('beforeupdate', this.onBeforeLoad, this);
7577 um.on('update', this.onLoad, this);
7578 um.on('failure', this.onLoad, this);
7579 this.removeMask = true;
7583 Roo.LoadMask.prototype = {
7585 * @cfg {Boolean} removeMask
7586 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7587 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7591 * The text to display in a centered loading message box (defaults to 'Loading...')
7595 * @cfg {String} msgCls
7596 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7598 msgCls : 'x-mask-loading',
7601 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7607 * Disables the mask to prevent it from being displayed
7609 disable : function(){
7610 this.disabled = true;
7614 * Enables the mask so that it can be displayed
7616 enable : function(){
7617 this.disabled = false;
7620 onLoadException : function()
7624 if (typeof(arguments[3]) != 'undefined') {
7625 Roo.MessageBox.alert("Error loading",arguments[3]);
7629 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7630 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7637 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7642 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7646 onBeforeLoad : function(){
7648 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7653 destroy : function(){
7655 this.store.un('beforeload', this.onBeforeLoad, this);
7656 this.store.un('load', this.onLoad, this);
7657 this.store.un('loadexception', this.onLoadException, this);
7659 var um = this.el.getUpdateManager();
7660 um.un('beforeupdate', this.onBeforeLoad, this);
7661 um.un('update', this.onLoad, this);
7662 um.un('failure', this.onLoad, this);
7673 * @class Roo.bootstrap.Table
7674 * @extends Roo.bootstrap.Component
7675 * Bootstrap Table class
7676 * @cfg {String} cls table class
7677 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7678 * @cfg {String} bgcolor Specifies the background color for a table
7679 * @cfg {Number} border Specifies whether the table cells should have borders or not
7680 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7681 * @cfg {Number} cellspacing Specifies the space between cells
7682 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7683 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7684 * @cfg {String} sortable Specifies that the table should be sortable
7685 * @cfg {String} summary Specifies a summary of the content of a table
7686 * @cfg {Number} width Specifies the width of a table
7687 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7689 * @cfg {boolean} striped Should the rows be alternative striped
7690 * @cfg {boolean} bordered Add borders to the table
7691 * @cfg {boolean} hover Add hover highlighting
7692 * @cfg {boolean} condensed Format condensed
7693 * @cfg {boolean} responsive Format condensed
7694 * @cfg {Boolean} loadMask (true|false) default false
7695 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7696 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7697 * @cfg {Boolean} rowSelection (true|false) default false
7698 * @cfg {Boolean} cellSelection (true|false) default false
7699 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7700 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7701 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7702 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7706 * Create a new Table
7707 * @param {Object} config The config object
7710 Roo.bootstrap.Table = function(config){
7711 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7716 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7717 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7718 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7719 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7721 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7723 this.sm.grid = this;
7724 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7725 this.sm = this.selModel;
7726 this.sm.xmodule = this.xmodule || false;
7729 if (this.cm && typeof(this.cm.config) == 'undefined') {
7730 this.colModel = new Roo.grid.ColumnModel(this.cm);
7731 this.cm = this.colModel;
7732 this.cm.xmodule = this.xmodule || false;
7735 this.store= Roo.factory(this.store, Roo.data);
7736 this.ds = this.store;
7737 this.ds.xmodule = this.xmodule || false;
7740 if (this.footer && this.store) {
7741 this.footer.dataSource = this.ds;
7742 this.footer = Roo.factory(this.footer);
7749 * Fires when a cell is clicked
7750 * @param {Roo.bootstrap.Table} this
7751 * @param {Roo.Element} el
7752 * @param {Number} rowIndex
7753 * @param {Number} columnIndex
7754 * @param {Roo.EventObject} e
7758 * @event celldblclick
7759 * Fires when a cell is double clicked
7760 * @param {Roo.bootstrap.Table} this
7761 * @param {Roo.Element} el
7762 * @param {Number} rowIndex
7763 * @param {Number} columnIndex
7764 * @param {Roo.EventObject} e
7766 "celldblclick" : true,
7769 * Fires when a row is clicked
7770 * @param {Roo.bootstrap.Table} this
7771 * @param {Roo.Element} el
7772 * @param {Number} rowIndex
7773 * @param {Roo.EventObject} e
7777 * @event rowdblclick
7778 * Fires when a row is double clicked
7779 * @param {Roo.bootstrap.Table} this
7780 * @param {Roo.Element} el
7781 * @param {Number} rowIndex
7782 * @param {Roo.EventObject} e
7784 "rowdblclick" : true,
7787 * Fires when a mouseover occur
7788 * @param {Roo.bootstrap.Table} this
7789 * @param {Roo.Element} el
7790 * @param {Number} rowIndex
7791 * @param {Number} columnIndex
7792 * @param {Roo.EventObject} e
7797 * Fires when a mouseout occur
7798 * @param {Roo.bootstrap.Table} this
7799 * @param {Roo.Element} el
7800 * @param {Number} rowIndex
7801 * @param {Number} columnIndex
7802 * @param {Roo.EventObject} e
7807 * Fires when a row is rendered, so you can change add a style to it.
7808 * @param {Roo.bootstrap.Table} this
7809 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7813 * @event rowsrendered
7814 * Fires when all the rows have been rendered
7815 * @param {Roo.bootstrap.Table} this
7817 'rowsrendered' : true,
7819 * @event contextmenu
7820 * The raw contextmenu event for the entire grid.
7821 * @param {Roo.EventObject} e
7823 "contextmenu" : true,
7825 * @event rowcontextmenu
7826 * Fires when a row is right clicked
7827 * @param {Roo.bootstrap.Table} this
7828 * @param {Number} rowIndex
7829 * @param {Roo.EventObject} e
7831 "rowcontextmenu" : true,
7833 * @event cellcontextmenu
7834 * Fires when a cell is right clicked
7835 * @param {Roo.bootstrap.Table} this
7836 * @param {Number} rowIndex
7837 * @param {Number} cellIndex
7838 * @param {Roo.EventObject} e
7840 "cellcontextmenu" : true,
7842 * @event headercontextmenu
7843 * Fires when a header is right clicked
7844 * @param {Roo.bootstrap.Table} this
7845 * @param {Number} columnIndex
7846 * @param {Roo.EventObject} e
7848 "headercontextmenu" : true
7852 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7878 rowSelection : false,
7879 cellSelection : false,
7882 // Roo.Element - the tbody
7884 // Roo.Element - thead element
7887 container: false, // used by gridpanel...
7893 auto_hide_footer : false,
7895 getAutoCreate : function()
7897 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7904 if (this.scrollBody) {
7905 cfg.cls += ' table-body-fixed';
7908 cfg.cls += ' table-striped';
7912 cfg.cls += ' table-hover';
7914 if (this.bordered) {
7915 cfg.cls += ' table-bordered';
7917 if (this.condensed) {
7918 cfg.cls += ' table-condensed';
7920 if (this.responsive) {
7921 cfg.cls += ' table-responsive';
7925 cfg.cls+= ' ' +this.cls;
7928 // this lot should be simplifed...
7941 ].forEach(function(k) {
7949 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7952 if(this.store || this.cm){
7953 if(this.headerShow){
7954 cfg.cn.push(this.renderHeader());
7957 cfg.cn.push(this.renderBody());
7959 if(this.footerShow){
7960 cfg.cn.push(this.renderFooter());
7962 // where does this come from?
7963 //cfg.cls+= ' TableGrid';
7966 return { cn : [ cfg ] };
7969 initEvents : function()
7971 if(!this.store || !this.cm){
7974 if (this.selModel) {
7975 this.selModel.initEvents();
7979 //Roo.log('initEvents with ds!!!!');
7981 this.mainBody = this.el.select('tbody', true).first();
7982 this.mainHead = this.el.select('thead', true).first();
7983 this.mainFoot = this.el.select('tfoot', true).first();
7989 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7990 e.on('click', _this.sort, _this);
7993 this.mainBody.on("click", this.onClick, this);
7994 this.mainBody.on("dblclick", this.onDblClick, this);
7996 // why is this done????? = it breaks dialogs??
7997 //this.parent().el.setStyle('position', 'relative');
8001 this.footer.parentId = this.id;
8002 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8005 this.el.select('tfoot tr td').first().addClass('hide');
8010 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8013 this.store.on('load', this.onLoad, this);
8014 this.store.on('beforeload', this.onBeforeLoad, this);
8015 this.store.on('update', this.onUpdate, this);
8016 this.store.on('add', this.onAdd, this);
8017 this.store.on("clear", this.clear, this);
8019 this.el.on("contextmenu", this.onContextMenu, this);
8021 this.mainBody.on('scroll', this.onBodyScroll, this);
8023 this.cm.on("headerchange", this.onHeaderChange, this);
8025 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8029 onContextMenu : function(e, t)
8031 this.processEvent("contextmenu", e);
8034 processEvent : function(name, e)
8036 if (name != 'touchstart' ) {
8037 this.fireEvent(name, e);
8040 var t = e.getTarget();
8042 var cell = Roo.get(t);
8048 if(cell.findParent('tfoot', false, true)){
8052 if(cell.findParent('thead', false, true)){
8054 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8055 cell = Roo.get(t).findParent('th', false, true);
8057 Roo.log("failed to find th in thead?");
8058 Roo.log(e.getTarget());
8063 var cellIndex = cell.dom.cellIndex;
8065 var ename = name == 'touchstart' ? 'click' : name;
8066 this.fireEvent("header" + ename, this, cellIndex, e);
8071 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8072 cell = Roo.get(t).findParent('td', false, true);
8074 Roo.log("failed to find th in tbody?");
8075 Roo.log(e.getTarget());
8080 var row = cell.findParent('tr', false, true);
8081 var cellIndex = cell.dom.cellIndex;
8082 var rowIndex = row.dom.rowIndex - 1;
8086 this.fireEvent("row" + name, this, rowIndex, e);
8090 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8096 onMouseover : function(e, el)
8098 var cell = Roo.get(el);
8104 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8105 cell = cell.findParent('td', false, true);
8108 var row = cell.findParent('tr', false, true);
8109 var cellIndex = cell.dom.cellIndex;
8110 var rowIndex = row.dom.rowIndex - 1; // start from 0
8112 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8116 onMouseout : function(e, el)
8118 var cell = Roo.get(el);
8124 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8125 cell = cell.findParent('td', false, true);
8128 var row = cell.findParent('tr', false, true);
8129 var cellIndex = cell.dom.cellIndex;
8130 var rowIndex = row.dom.rowIndex - 1; // start from 0
8132 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8136 onClick : function(e, el)
8138 var cell = Roo.get(el);
8140 if(!cell || (!this.cellSelection && !this.rowSelection)){
8144 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8145 cell = cell.findParent('td', false, true);
8148 if(!cell || typeof(cell) == 'undefined'){
8152 var row = cell.findParent('tr', false, true);
8154 if(!row || typeof(row) == 'undefined'){
8158 var cellIndex = cell.dom.cellIndex;
8159 var rowIndex = this.getRowIndex(row);
8161 // why??? - should these not be based on SelectionModel?
8162 if(this.cellSelection){
8163 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8166 if(this.rowSelection){
8167 this.fireEvent('rowclick', this, row, rowIndex, e);
8173 onDblClick : function(e,el)
8175 var cell = Roo.get(el);
8177 if(!cell || (!this.cellSelection && !this.rowSelection)){
8181 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8182 cell = cell.findParent('td', false, true);
8185 if(!cell || typeof(cell) == 'undefined'){
8189 var row = cell.findParent('tr', false, true);
8191 if(!row || typeof(row) == 'undefined'){
8195 var cellIndex = cell.dom.cellIndex;
8196 var rowIndex = this.getRowIndex(row);
8198 if(this.cellSelection){
8199 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8202 if(this.rowSelection){
8203 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8207 sort : function(e,el)
8209 var col = Roo.get(el);
8211 if(!col.hasClass('sortable')){
8215 var sort = col.attr('sort');
8218 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8222 this.store.sortInfo = {field : sort, direction : dir};
8225 Roo.log("calling footer first");
8226 this.footer.onClick('first');
8229 this.store.load({ params : { start : 0 } });
8233 renderHeader : function()
8241 this.totalWidth = 0;
8243 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8245 var config = cm.config[i];
8249 cls : 'x-hcol-' + i,
8251 html: cm.getColumnHeader(i)
8256 if(typeof(config.sortable) != 'undefined' && config.sortable){
8258 c.html = '<i class="glyphicon"></i>' + c.html;
8261 // could use BS4 hidden-..-down
8263 if(typeof(config.lgHeader) != 'undefined'){
8264 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8267 if(typeof(config.mdHeader) != 'undefined'){
8268 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8271 if(typeof(config.smHeader) != 'undefined'){
8272 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8275 if(typeof(config.xsHeader) != 'undefined'){
8276 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8283 if(typeof(config.tooltip) != 'undefined'){
8284 c.tooltip = config.tooltip;
8287 if(typeof(config.colspan) != 'undefined'){
8288 c.colspan = config.colspan;
8291 if(typeof(config.hidden) != 'undefined' && config.hidden){
8292 c.style += ' display:none;';
8295 if(typeof(config.dataIndex) != 'undefined'){
8296 c.sort = config.dataIndex;
8301 if(typeof(config.align) != 'undefined' && config.align.length){
8302 c.style += ' text-align:' + config.align + ';';
8305 if(typeof(config.width) != 'undefined'){
8306 c.style += ' width:' + config.width + 'px;';
8307 this.totalWidth += config.width;
8309 this.totalWidth += 100; // assume minimum of 100 per column?
8312 if(typeof(config.cls) != 'undefined'){
8313 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8316 ['xs','sm','md','lg'].map(function(size){
8318 if(typeof(config[size]) == 'undefined'){
8322 if (!config[size]) { // 0 = hidden
8323 // BS 4 '0' is treated as hide that column and below.
8324 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8328 c.cls += ' col-' + size + '-' + config[size] + (
8329 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8341 renderBody : function()
8351 colspan : this.cm.getColumnCount()
8361 renderFooter : function()
8371 colspan : this.cm.getColumnCount()
8385 // Roo.log('ds onload');
8390 var ds = this.store;
8392 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8393 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8394 if (_this.store.sortInfo) {
8396 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8397 e.select('i', true).addClass(['glyphicon-arrow-up']);
8400 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8401 e.select('i', true).addClass(['glyphicon-arrow-down']);
8406 var tbody = this.mainBody;
8408 if(ds.getCount() > 0){
8409 ds.data.each(function(d,rowIndex){
8410 var row = this.renderRow(cm, ds, rowIndex);
8412 tbody.createChild(row);
8416 if(row.cellObjects.length){
8417 Roo.each(row.cellObjects, function(r){
8418 _this.renderCellObject(r);
8425 var tfoot = this.el.select('tfoot', true).first();
8427 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8429 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8431 var total = this.ds.getTotalCount();
8433 if(this.footer.pageSize < total){
8434 this.mainFoot.show();
8438 Roo.each(this.el.select('tbody td', true).elements, function(e){
8439 e.on('mouseover', _this.onMouseover, _this);
8442 Roo.each(this.el.select('tbody td', true).elements, function(e){
8443 e.on('mouseout', _this.onMouseout, _this);
8445 this.fireEvent('rowsrendered', this);
8451 onUpdate : function(ds,record)
8453 this.refreshRow(record);
8457 onRemove : function(ds, record, index, isUpdate){
8458 if(isUpdate !== true){
8459 this.fireEvent("beforerowremoved", this, index, record);
8461 var bt = this.mainBody.dom;
8463 var rows = this.el.select('tbody > tr', true).elements;
8465 if(typeof(rows[index]) != 'undefined'){
8466 bt.removeChild(rows[index].dom);
8469 // if(bt.rows[index]){
8470 // bt.removeChild(bt.rows[index]);
8473 if(isUpdate !== true){
8474 //this.stripeRows(index);
8475 //this.syncRowHeights(index, index);
8477 this.fireEvent("rowremoved", this, index, record);
8481 onAdd : function(ds, records, rowIndex)
8483 //Roo.log('on Add called');
8484 // - note this does not handle multiple adding very well..
8485 var bt = this.mainBody.dom;
8486 for (var i =0 ; i < records.length;i++) {
8487 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8488 //Roo.log(records[i]);
8489 //Roo.log(this.store.getAt(rowIndex+i));
8490 this.insertRow(this.store, rowIndex + i, false);
8497 refreshRow : function(record){
8498 var ds = this.store, index;
8499 if(typeof record == 'number'){
8501 record = ds.getAt(index);
8503 index = ds.indexOf(record);
8505 return; // should not happen - but seems to
8508 this.insertRow(ds, index, true);
8510 this.onRemove(ds, record, index+1, true);
8512 //this.syncRowHeights(index, index);
8514 this.fireEvent("rowupdated", this, index, record);
8517 insertRow : function(dm, rowIndex, isUpdate){
8520 this.fireEvent("beforerowsinserted", this, rowIndex);
8522 //var s = this.getScrollState();
8523 var row = this.renderRow(this.cm, this.store, rowIndex);
8524 // insert before rowIndex..
8525 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8529 if(row.cellObjects.length){
8530 Roo.each(row.cellObjects, function(r){
8531 _this.renderCellObject(r);
8536 this.fireEvent("rowsinserted", this, rowIndex);
8537 //this.syncRowHeights(firstRow, lastRow);
8538 //this.stripeRows(firstRow);
8545 getRowDom : function(rowIndex)
8547 var rows = this.el.select('tbody > tr', true).elements;
8549 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8552 // returns the object tree for a tr..
8555 renderRow : function(cm, ds, rowIndex)
8557 var d = ds.getAt(rowIndex);
8561 cls : 'x-row-' + rowIndex,
8565 var cellObjects = [];
8567 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8568 var config = cm.config[i];
8570 var renderer = cm.getRenderer(i);
8574 if(typeof(renderer) !== 'undefined'){
8575 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8577 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8578 // and are rendered into the cells after the row is rendered - using the id for the element.
8580 if(typeof(value) === 'object'){
8590 rowIndex : rowIndex,
8595 this.fireEvent('rowclass', this, rowcfg);
8599 cls : rowcfg.rowClass + ' x-col-' + i,
8601 html: (typeof(value) === 'object') ? '' : value
8608 if(typeof(config.colspan) != 'undefined'){
8609 td.colspan = config.colspan;
8612 if(typeof(config.hidden) != 'undefined' && config.hidden){
8613 td.style += ' display:none;';
8616 if(typeof(config.align) != 'undefined' && config.align.length){
8617 td.style += ' text-align:' + config.align + ';';
8619 if(typeof(config.valign) != 'undefined' && config.valign.length){
8620 td.style += ' vertical-align:' + config.valign + ';';
8623 if(typeof(config.width) != 'undefined'){
8624 td.style += ' width:' + config.width + 'px;';
8627 if(typeof(config.cursor) != 'undefined'){
8628 td.style += ' cursor:' + config.cursor + ';';
8631 if(typeof(config.cls) != 'undefined'){
8632 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8635 ['xs','sm','md','lg'].map(function(size){
8637 if(typeof(config[size]) == 'undefined'){
8643 if (!config[size]) { // 0 = hidden
8644 // BS 4 '0' is treated as hide that column and below.
8645 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8649 td.cls += ' col-' + size + '-' + config[size] + (
8650 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8660 row.cellObjects = cellObjects;
8668 onBeforeLoad : function()
8677 this.el.select('tbody', true).first().dom.innerHTML = '';
8680 * Show or hide a row.
8681 * @param {Number} rowIndex to show or hide
8682 * @param {Boolean} state hide
8684 setRowVisibility : function(rowIndex, state)
8686 var bt = this.mainBody.dom;
8688 var rows = this.el.select('tbody > tr', true).elements;
8690 if(typeof(rows[rowIndex]) == 'undefined'){
8693 rows[rowIndex].dom.style.display = state ? '' : 'none';
8697 getSelectionModel : function(){
8699 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8701 return this.selModel;
8704 * Render the Roo.bootstrap object from renderder
8706 renderCellObject : function(r)
8710 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8712 var t = r.cfg.render(r.container);
8715 Roo.each(r.cfg.cn, function(c){
8717 container: t.getChildContainer(),
8720 _this.renderCellObject(child);
8725 getRowIndex : function(row)
8729 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8740 * Returns the grid's underlying element = used by panel.Grid
8741 * @return {Element} The element
8743 getGridEl : function(){
8747 * Forces a resize - used by panel.Grid
8748 * @return {Element} The element
8750 autoSize : function()
8752 //var ctr = Roo.get(this.container.dom.parentElement);
8753 var ctr = Roo.get(this.el.dom);
8755 var thd = this.getGridEl().select('thead',true).first();
8756 var tbd = this.getGridEl().select('tbody', true).first();
8757 var tfd = this.getGridEl().select('tfoot', true).first();
8759 var cw = ctr.getWidth();
8760 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8764 tbd.setWidth(ctr.getWidth());
8765 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8766 // this needs fixing for various usage - currently only hydra job advers I think..
8768 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8770 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8773 cw = Math.max(cw, this.totalWidth);
8774 this.getGridEl().select('tbody tr',true).setWidth(cw);
8776 // resize 'expandable coloumn?
8778 return; // we doe not have a view in this design..
8781 onBodyScroll: function()
8783 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8785 this.mainHead.setStyle({
8786 'position' : 'relative',
8787 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8793 var scrollHeight = this.mainBody.dom.scrollHeight;
8795 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8797 var height = this.mainBody.getHeight();
8799 if(scrollHeight - height == scrollTop) {
8801 var total = this.ds.getTotalCount();
8803 if(this.footer.cursor + this.footer.pageSize < total){
8805 this.footer.ds.load({
8807 start : this.footer.cursor + this.footer.pageSize,
8808 limit : this.footer.pageSize
8818 onHeaderChange : function()
8820 var header = this.renderHeader();
8821 var table = this.el.select('table', true).first();
8823 this.mainHead.remove();
8824 this.mainHead = table.createChild(header, this.mainBody, false);
8827 onHiddenChange : function(colModel, colIndex, hidden)
8829 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8830 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8832 this.CSS.updateRule(thSelector, "display", "");
8833 this.CSS.updateRule(tdSelector, "display", "");
8836 this.CSS.updateRule(thSelector, "display", "none");
8837 this.CSS.updateRule(tdSelector, "display", "none");
8840 this.onHeaderChange();
8844 setColumnWidth: function(col_index, width)
8846 // width = "md-2 xs-2..."
8847 if(!this.colModel.config[col_index]) {
8851 var w = width.split(" ");
8853 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8855 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8858 for(var j = 0; j < w.length; j++) {
8864 var size_cls = w[j].split("-");
8866 if(!Number.isInteger(size_cls[1] * 1)) {
8870 if(!this.colModel.config[col_index][size_cls[0]]) {
8874 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8878 h_row[0].classList.replace(
8879 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8880 "col-"+size_cls[0]+"-"+size_cls[1]
8883 for(var i = 0; i < rows.length; i++) {
8885 var size_cls = w[j].split("-");
8887 if(!Number.isInteger(size_cls[1] * 1)) {
8891 if(!this.colModel.config[col_index][size_cls[0]]) {
8895 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8899 rows[i].classList.replace(
8900 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8901 "col-"+size_cls[0]+"-"+size_cls[1]
8905 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8920 * @class Roo.bootstrap.TableCell
8921 * @extends Roo.bootstrap.Component
8922 * Bootstrap TableCell class
8923 * @cfg {String} html cell contain text
8924 * @cfg {String} cls cell class
8925 * @cfg {String} tag cell tag (td|th) default td
8926 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8927 * @cfg {String} align Aligns the content in a cell
8928 * @cfg {String} axis Categorizes cells
8929 * @cfg {String} bgcolor Specifies the background color of a cell
8930 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8931 * @cfg {Number} colspan Specifies the number of columns a cell should span
8932 * @cfg {String} headers Specifies one or more header cells a cell is related to
8933 * @cfg {Number} height Sets the height of a cell
8934 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8935 * @cfg {Number} rowspan Sets the number of rows a cell should span
8936 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8937 * @cfg {String} valign Vertical aligns the content in a cell
8938 * @cfg {Number} width Specifies the width of a cell
8941 * Create a new TableCell
8942 * @param {Object} config The config object
8945 Roo.bootstrap.TableCell = function(config){
8946 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8949 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8969 getAutoCreate : function(){
8970 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8990 cfg.align=this.align
8996 cfg.bgcolor=this.bgcolor
8999 cfg.charoff=this.charoff
9002 cfg.colspan=this.colspan
9005 cfg.headers=this.headers
9008 cfg.height=this.height
9011 cfg.nowrap=this.nowrap
9014 cfg.rowspan=this.rowspan
9017 cfg.scope=this.scope
9020 cfg.valign=this.valign
9023 cfg.width=this.width
9042 * @class Roo.bootstrap.TableRow
9043 * @extends Roo.bootstrap.Component
9044 * Bootstrap TableRow class
9045 * @cfg {String} cls row class
9046 * @cfg {String} align Aligns the content in a table row
9047 * @cfg {String} bgcolor Specifies a background color for a table row
9048 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9049 * @cfg {String} valign Vertical aligns the content in a table row
9052 * Create a new TableRow
9053 * @param {Object} config The config object
9056 Roo.bootstrap.TableRow = function(config){
9057 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9060 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9068 getAutoCreate : function(){
9069 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9079 cfg.align = this.align;
9082 cfg.bgcolor = this.bgcolor;
9085 cfg.charoff = this.charoff;
9088 cfg.valign = this.valign;
9106 * @class Roo.bootstrap.TableBody
9107 * @extends Roo.bootstrap.Component
9108 * Bootstrap TableBody class
9109 * @cfg {String} cls element class
9110 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9111 * @cfg {String} align Aligns the content inside the element
9112 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9113 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9116 * Create a new TableBody
9117 * @param {Object} config The config object
9120 Roo.bootstrap.TableBody = function(config){
9121 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9124 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9132 getAutoCreate : function(){
9133 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9147 cfg.align = this.align;
9150 cfg.charoff = this.charoff;
9153 cfg.valign = this.valign;
9160 // initEvents : function()
9167 // this.store = Roo.factory(this.store, Roo.data);
9168 // this.store.on('load', this.onLoad, this);
9170 // this.store.load();
9174 // onLoad: function ()
9176 // this.fireEvent('load', this);
9186 * Ext JS Library 1.1.1
9187 * Copyright(c) 2006-2007, Ext JS, LLC.
9189 * Originally Released Under LGPL - original licence link has changed is not relivant.
9192 * <script type="text/javascript">
9195 // as we use this in bootstrap.
9196 Roo.namespace('Roo.form');
9198 * @class Roo.form.Action
9199 * Internal Class used to handle form actions
9201 * @param {Roo.form.BasicForm} el The form element or its id
9202 * @param {Object} config Configuration options
9207 // define the action interface
9208 Roo.form.Action = function(form, options){
9210 this.options = options || {};
9213 * Client Validation Failed
9216 Roo.form.Action.CLIENT_INVALID = 'client';
9218 * Server Validation Failed
9221 Roo.form.Action.SERVER_INVALID = 'server';
9223 * Connect to Server Failed
9226 Roo.form.Action.CONNECT_FAILURE = 'connect';
9228 * Reading Data from Server Failed
9231 Roo.form.Action.LOAD_FAILURE = 'load';
9233 Roo.form.Action.prototype = {
9235 failureType : undefined,
9236 response : undefined,
9240 run : function(options){
9245 success : function(response){
9250 handleResponse : function(response){
9254 // default connection failure
9255 failure : function(response){
9257 this.response = response;
9258 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9259 this.form.afterAction(this, false);
9262 processResponse : function(response){
9263 this.response = response;
9264 if(!response.responseText){
9267 this.result = this.handleResponse(response);
9271 // utility functions used internally
9272 getUrl : function(appendParams){
9273 var url = this.options.url || this.form.url || this.form.el.dom.action;
9275 var p = this.getParams();
9277 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9283 getMethod : function(){
9284 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9287 getParams : function(){
9288 var bp = this.form.baseParams;
9289 var p = this.options.params;
9291 if(typeof p == "object"){
9292 p = Roo.urlEncode(Roo.applyIf(p, bp));
9293 }else if(typeof p == 'string' && bp){
9294 p += '&' + Roo.urlEncode(bp);
9297 p = Roo.urlEncode(bp);
9302 createCallback : function(){
9304 success: this.success,
9305 failure: this.failure,
9307 timeout: (this.form.timeout*1000),
9308 upload: this.form.fileUpload ? this.success : undefined
9313 Roo.form.Action.Submit = function(form, options){
9314 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9317 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9320 haveProgress : false,
9321 uploadComplete : false,
9323 // uploadProgress indicator.
9324 uploadProgress : function()
9326 if (!this.form.progressUrl) {
9330 if (!this.haveProgress) {
9331 Roo.MessageBox.progress("Uploading", "Uploading");
9333 if (this.uploadComplete) {
9334 Roo.MessageBox.hide();
9338 this.haveProgress = true;
9340 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9342 var c = new Roo.data.Connection();
9344 url : this.form.progressUrl,
9349 success : function(req){
9350 //console.log(data);
9354 rdata = Roo.decode(req.responseText)
9356 Roo.log("Invalid data from server..");
9360 if (!rdata || !rdata.success) {
9362 Roo.MessageBox.alert(Roo.encode(rdata));
9365 var data = rdata.data;
9367 if (this.uploadComplete) {
9368 Roo.MessageBox.hide();
9373 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9374 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9377 this.uploadProgress.defer(2000,this);
9380 failure: function(data) {
9381 Roo.log('progress url failed ');
9392 // run get Values on the form, so it syncs any secondary forms.
9393 this.form.getValues();
9395 var o = this.options;
9396 var method = this.getMethod();
9397 var isPost = method == 'POST';
9398 if(o.clientValidation === false || this.form.isValid()){
9400 if (this.form.progressUrl) {
9401 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9402 (new Date() * 1) + '' + Math.random());
9407 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9408 form:this.form.el.dom,
9409 url:this.getUrl(!isPost),
9411 params:isPost ? this.getParams() : null,
9412 isUpload: this.form.fileUpload,
9413 formData : this.form.formData
9416 this.uploadProgress();
9418 }else if (o.clientValidation !== false){ // client validation failed
9419 this.failureType = Roo.form.Action.CLIENT_INVALID;
9420 this.form.afterAction(this, false);
9424 success : function(response)
9426 this.uploadComplete= true;
9427 if (this.haveProgress) {
9428 Roo.MessageBox.hide();
9432 var result = this.processResponse(response);
9433 if(result === true || result.success){
9434 this.form.afterAction(this, true);
9438 this.form.markInvalid(result.errors);
9439 this.failureType = Roo.form.Action.SERVER_INVALID;
9441 this.form.afterAction(this, false);
9443 failure : function(response)
9445 this.uploadComplete= true;
9446 if (this.haveProgress) {
9447 Roo.MessageBox.hide();
9450 this.response = response;
9451 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9452 this.form.afterAction(this, false);
9455 handleResponse : function(response){
9456 if(this.form.errorReader){
9457 var rs = this.form.errorReader.read(response);
9460 for(var i = 0, len = rs.records.length; i < len; i++) {
9461 var r = rs.records[i];
9465 if(errors.length < 1){
9469 success : rs.success,
9475 ret = Roo.decode(response.responseText);
9479 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9489 Roo.form.Action.Load = function(form, options){
9490 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9491 this.reader = this.form.reader;
9494 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9499 Roo.Ajax.request(Roo.apply(
9500 this.createCallback(), {
9501 method:this.getMethod(),
9502 url:this.getUrl(false),
9503 params:this.getParams()
9507 success : function(response){
9509 var result = this.processResponse(response);
9510 if(result === true || !result.success || !result.data){
9511 this.failureType = Roo.form.Action.LOAD_FAILURE;
9512 this.form.afterAction(this, false);
9515 this.form.clearInvalid();
9516 this.form.setValues(result.data);
9517 this.form.afterAction(this, true);
9520 handleResponse : function(response){
9521 if(this.form.reader){
9522 var rs = this.form.reader.read(response);
9523 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9525 success : rs.success,
9529 return Roo.decode(response.responseText);
9533 Roo.form.Action.ACTION_TYPES = {
9534 'load' : Roo.form.Action.Load,
9535 'submit' : Roo.form.Action.Submit
9544 * @class Roo.bootstrap.Form
9545 * @extends Roo.bootstrap.Component
9546 * Bootstrap Form class
9547 * @cfg {String} method GET | POST (default POST)
9548 * @cfg {String} labelAlign top | left (default top)
9549 * @cfg {String} align left | right - for navbars
9550 * @cfg {Boolean} loadMask load mask when submit (default true)
9555 * @param {Object} config The config object
9559 Roo.bootstrap.Form = function(config){
9561 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9563 Roo.bootstrap.Form.popover.apply();
9567 * @event clientvalidation
9568 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9569 * @param {Form} this
9570 * @param {Boolean} valid true if the form has passed client-side validation
9572 clientvalidation: true,
9574 * @event beforeaction
9575 * Fires before any action is performed. Return false to cancel the action.
9576 * @param {Form} this
9577 * @param {Action} action The action to be performed
9581 * @event actionfailed
9582 * Fires when an action fails.
9583 * @param {Form} this
9584 * @param {Action} action The action that failed
9586 actionfailed : true,
9588 * @event actioncomplete
9589 * Fires when an action is completed.
9590 * @param {Form} this
9591 * @param {Action} action The action that completed
9593 actioncomplete : true
9597 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9600 * @cfg {String} method
9601 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9606 * The URL to use for form actions if one isn't supplied in the action options.
9609 * @cfg {Boolean} fileUpload
9610 * Set to true if this form is a file upload.
9614 * @cfg {Object} baseParams
9615 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9619 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9623 * @cfg {Sting} align (left|right) for navbar forms
9628 activeAction : null,
9631 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9632 * element by passing it or its id or mask the form itself by passing in true.
9635 waitMsgTarget : false,
9640 * @cfg {Boolean} errorMask (true|false) default false
9645 * @cfg {Number} maskOffset Default 100
9650 * @cfg {Boolean} maskBody
9654 getAutoCreate : function(){
9658 method : this.method || 'POST',
9659 id : this.id || Roo.id(),
9662 if (this.parent().xtype.match(/^Nav/)) {
9663 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9667 if (this.labelAlign == 'left' ) {
9668 cfg.cls += ' form-horizontal';
9674 initEvents : function()
9676 this.el.on('submit', this.onSubmit, this);
9677 // this was added as random key presses on the form where triggering form submit.
9678 this.el.on('keypress', function(e) {
9679 if (e.getCharCode() != 13) {
9682 // we might need to allow it for textareas.. and some other items.
9683 // check e.getTarget().
9685 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9689 Roo.log("keypress blocked");
9697 onSubmit : function(e){
9702 * Returns true if client-side validation on the form is successful.
9705 isValid : function(){
9706 var items = this.getItems();
9710 items.each(function(f){
9716 Roo.log('invalid field: ' + f.name);
9720 if(!target && f.el.isVisible(true)){
9726 if(this.errorMask && !valid){
9727 Roo.bootstrap.Form.popover.mask(this, target);
9734 * Returns true if any fields in this form have changed since their original load.
9737 isDirty : function(){
9739 var items = this.getItems();
9740 items.each(function(f){
9750 * Performs a predefined action (submit or load) or custom actions you define on this form.
9751 * @param {String} actionName The name of the action type
9752 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9753 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9754 * accept other config options):
9756 Property Type Description
9757 ---------------- --------------- ----------------------------------------------------------------------------------
9758 url String The url for the action (defaults to the form's url)
9759 method String The form method to use (defaults to the form's method, or POST if not defined)
9760 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9761 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9762 validate the form on the client (defaults to false)
9764 * @return {BasicForm} this
9766 doAction : function(action, options){
9767 if(typeof action == 'string'){
9768 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9770 if(this.fireEvent('beforeaction', this, action) !== false){
9771 this.beforeAction(action);
9772 action.run.defer(100, action);
9778 beforeAction : function(action){
9779 var o = action.options;
9784 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9786 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9789 // not really supported yet.. ??
9791 //if(this.waitMsgTarget === true){
9792 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9793 //}else if(this.waitMsgTarget){
9794 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9795 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9797 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9803 afterAction : function(action, success){
9804 this.activeAction = null;
9805 var o = action.options;
9810 Roo.get(document.body).unmask();
9816 //if(this.waitMsgTarget === true){
9817 // this.el.unmask();
9818 //}else if(this.waitMsgTarget){
9819 // this.waitMsgTarget.unmask();
9821 // Roo.MessageBox.updateProgress(1);
9822 // Roo.MessageBox.hide();
9829 Roo.callback(o.success, o.scope, [this, action]);
9830 this.fireEvent('actioncomplete', this, action);
9834 // failure condition..
9835 // we have a scenario where updates need confirming.
9836 // eg. if a locking scenario exists..
9837 // we look for { errors : { needs_confirm : true }} in the response.
9839 (typeof(action.result) != 'undefined') &&
9840 (typeof(action.result.errors) != 'undefined') &&
9841 (typeof(action.result.errors.needs_confirm) != 'undefined')
9844 Roo.log("not supported yet");
9847 Roo.MessageBox.confirm(
9848 "Change requires confirmation",
9849 action.result.errorMsg,
9854 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9864 Roo.callback(o.failure, o.scope, [this, action]);
9865 // show an error message if no failed handler is set..
9866 if (!this.hasListener('actionfailed')) {
9867 Roo.log("need to add dialog support");
9869 Roo.MessageBox.alert("Error",
9870 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9871 action.result.errorMsg :
9872 "Saving Failed, please check your entries or try again"
9877 this.fireEvent('actionfailed', this, action);
9882 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9883 * @param {String} id The value to search for
9886 findField : function(id){
9887 var items = this.getItems();
9888 var field = items.get(id);
9890 items.each(function(f){
9891 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9898 return field || null;
9901 * Mark fields in this form invalid in bulk.
9902 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9903 * @return {BasicForm} this
9905 markInvalid : function(errors){
9906 if(errors instanceof Array){
9907 for(var i = 0, len = errors.length; i < len; i++){
9908 var fieldError = errors[i];
9909 var f = this.findField(fieldError.id);
9911 f.markInvalid(fieldError.msg);
9917 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9918 field.markInvalid(errors[id]);
9922 //Roo.each(this.childForms || [], function (f) {
9923 // f.markInvalid(errors);
9930 * Set values for fields in this form in bulk.
9931 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9932 * @return {BasicForm} this
9934 setValues : function(values){
9935 if(values instanceof Array){ // array of objects
9936 for(var i = 0, len = values.length; i < len; i++){
9938 var f = this.findField(v.id);
9940 f.setValue(v.value);
9941 if(this.trackResetOnLoad){
9942 f.originalValue = f.getValue();
9946 }else{ // object hash
9949 if(typeof values[id] != 'function' && (field = this.findField(id))){
9951 if (field.setFromData &&
9953 field.displayField &&
9954 // combos' with local stores can
9955 // be queried via setValue()
9956 // to set their value..
9957 (field.store && !field.store.isLocal)
9961 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9962 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9963 field.setFromData(sd);
9965 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9967 field.setFromData(values);
9970 field.setValue(values[id]);
9974 if(this.trackResetOnLoad){
9975 field.originalValue = field.getValue();
9981 //Roo.each(this.childForms || [], function (f) {
9982 // f.setValues(values);
9989 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9990 * they are returned as an array.
9991 * @param {Boolean} asString
9994 getValues : function(asString){
9995 //if (this.childForms) {
9996 // copy values from the child forms
9997 // Roo.each(this.childForms, function (f) {
9998 // this.setValues(f.getValues());
10004 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10005 if(asString === true){
10008 return Roo.urlDecode(fs);
10012 * Returns the fields in this form as an object with key/value pairs.
10013 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10016 getFieldValues : function(with_hidden)
10018 var items = this.getItems();
10020 items.each(function(f){
10022 if (!f.getName()) {
10026 var v = f.getValue();
10028 if (f.inputType =='radio') {
10029 if (typeof(ret[f.getName()]) == 'undefined') {
10030 ret[f.getName()] = ''; // empty..
10033 if (!f.el.dom.checked) {
10037 v = f.el.dom.value;
10041 if(f.xtype == 'MoneyField'){
10042 ret[f.currencyName] = f.getCurrency();
10045 // not sure if this supported any more..
10046 if ((typeof(v) == 'object') && f.getRawValue) {
10047 v = f.getRawValue() ; // dates..
10049 // combo boxes where name != hiddenName...
10050 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10051 ret[f.name] = f.getRawValue();
10053 ret[f.getName()] = v;
10060 * Clears all invalid messages in this form.
10061 * @return {BasicForm} this
10063 clearInvalid : function(){
10064 var items = this.getItems();
10066 items.each(function(f){
10074 * Resets this form.
10075 * @return {BasicForm} this
10077 reset : function(){
10078 var items = this.getItems();
10079 items.each(function(f){
10083 Roo.each(this.childForms || [], function (f) {
10091 getItems : function()
10093 var r=new Roo.util.MixedCollection(false, function(o){
10094 return o.id || (o.id = Roo.id());
10096 var iter = function(el) {
10103 Roo.each(el.items,function(e) {
10112 hideFields : function(items)
10114 Roo.each(items, function(i){
10116 var f = this.findField(i);
10127 showFields : function(items)
10129 Roo.each(items, function(i){
10131 var f = this.findField(i);
10144 Roo.apply(Roo.bootstrap.Form, {
10160 intervalID : false,
10166 if(this.isApplied){
10171 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10172 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10173 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10174 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10177 this.maskEl.top.enableDisplayMode("block");
10178 this.maskEl.left.enableDisplayMode("block");
10179 this.maskEl.bottom.enableDisplayMode("block");
10180 this.maskEl.right.enableDisplayMode("block");
10182 this.toolTip = new Roo.bootstrap.Tooltip({
10183 cls : 'roo-form-error-popover',
10185 'left' : ['r-l', [-2,0], 'right'],
10186 'right' : ['l-r', [2,0], 'left'],
10187 'bottom' : ['tl-bl', [0,2], 'top'],
10188 'top' : [ 'bl-tl', [0,-2], 'bottom']
10192 this.toolTip.render(Roo.get(document.body));
10194 this.toolTip.el.enableDisplayMode("block");
10196 Roo.get(document.body).on('click', function(){
10200 Roo.get(document.body).on('touchstart', function(){
10204 this.isApplied = true
10207 mask : function(form, target)
10211 this.target = target;
10213 if(!this.form.errorMask || !target.el){
10217 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10219 Roo.log(scrollable);
10221 var ot = this.target.el.calcOffsetsTo(scrollable);
10223 var scrollTo = ot[1] - this.form.maskOffset;
10225 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10227 scrollable.scrollTo('top', scrollTo);
10229 var box = this.target.el.getBox();
10231 var zIndex = Roo.bootstrap.Modal.zIndex++;
10234 this.maskEl.top.setStyle('position', 'absolute');
10235 this.maskEl.top.setStyle('z-index', zIndex);
10236 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10237 this.maskEl.top.setLeft(0);
10238 this.maskEl.top.setTop(0);
10239 this.maskEl.top.show();
10241 this.maskEl.left.setStyle('position', 'absolute');
10242 this.maskEl.left.setStyle('z-index', zIndex);
10243 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10244 this.maskEl.left.setLeft(0);
10245 this.maskEl.left.setTop(box.y - this.padding);
10246 this.maskEl.left.show();
10248 this.maskEl.bottom.setStyle('position', 'absolute');
10249 this.maskEl.bottom.setStyle('z-index', zIndex);
10250 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10251 this.maskEl.bottom.setLeft(0);
10252 this.maskEl.bottom.setTop(box.bottom + this.padding);
10253 this.maskEl.bottom.show();
10255 this.maskEl.right.setStyle('position', 'absolute');
10256 this.maskEl.right.setStyle('z-index', zIndex);
10257 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10258 this.maskEl.right.setLeft(box.right + this.padding);
10259 this.maskEl.right.setTop(box.y - this.padding);
10260 this.maskEl.right.show();
10262 this.toolTip.bindEl = this.target.el;
10264 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10266 var tip = this.target.blankText;
10268 if(this.target.getValue() !== '' ) {
10270 if (this.target.invalidText.length) {
10271 tip = this.target.invalidText;
10272 } else if (this.target.regexText.length){
10273 tip = this.target.regexText;
10277 this.toolTip.show(tip);
10279 this.intervalID = window.setInterval(function() {
10280 Roo.bootstrap.Form.popover.unmask();
10283 window.onwheel = function(){ return false;};
10285 (function(){ this.isMasked = true; }).defer(500, this);
10289 unmask : function()
10291 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10295 this.maskEl.top.setStyle('position', 'absolute');
10296 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10297 this.maskEl.top.hide();
10299 this.maskEl.left.setStyle('position', 'absolute');
10300 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10301 this.maskEl.left.hide();
10303 this.maskEl.bottom.setStyle('position', 'absolute');
10304 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10305 this.maskEl.bottom.hide();
10307 this.maskEl.right.setStyle('position', 'absolute');
10308 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10309 this.maskEl.right.hide();
10311 this.toolTip.hide();
10313 this.toolTip.el.hide();
10315 window.onwheel = function(){ return true;};
10317 if(this.intervalID){
10318 window.clearInterval(this.intervalID);
10319 this.intervalID = false;
10322 this.isMasked = false;
10332 * Ext JS Library 1.1.1
10333 * Copyright(c) 2006-2007, Ext JS, LLC.
10335 * Originally Released Under LGPL - original licence link has changed is not relivant.
10338 * <script type="text/javascript">
10341 * @class Roo.form.VTypes
10342 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10345 Roo.form.VTypes = function(){
10346 // closure these in so they are only created once.
10347 var alpha = /^[a-zA-Z_]+$/;
10348 var alphanum = /^[a-zA-Z0-9_]+$/;
10349 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10350 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10352 // All these messages and functions are configurable
10355 * The function used to validate email addresses
10356 * @param {String} value The email address
10358 'email' : function(v){
10359 return email.test(v);
10362 * The error text to display when the email validation function returns false
10365 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10367 * The keystroke filter mask to be applied on email input
10370 'emailMask' : /[a-z0-9_\.\-@]/i,
10373 * The function used to validate URLs
10374 * @param {String} value The URL
10376 'url' : function(v){
10377 return url.test(v);
10380 * The error text to display when the url validation function returns false
10383 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10386 * The function used to validate alpha values
10387 * @param {String} value The value
10389 'alpha' : function(v){
10390 return alpha.test(v);
10393 * The error text to display when the alpha validation function returns false
10396 'alphaText' : 'This field should only contain letters and _',
10398 * The keystroke filter mask to be applied on alpha input
10401 'alphaMask' : /[a-z_]/i,
10404 * The function used to validate alphanumeric values
10405 * @param {String} value The value
10407 'alphanum' : function(v){
10408 return alphanum.test(v);
10411 * The error text to display when the alphanumeric validation function returns false
10414 'alphanumText' : 'This field should only contain letters, numbers and _',
10416 * The keystroke filter mask to be applied on alphanumeric input
10419 'alphanumMask' : /[a-z0-9_]/i
10429 * @class Roo.bootstrap.Input
10430 * @extends Roo.bootstrap.Component
10431 * Bootstrap Input class
10432 * @cfg {Boolean} disabled is it disabled
10433 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10434 * @cfg {String} name name of the input
10435 * @cfg {string} fieldLabel - the label associated
10436 * @cfg {string} placeholder - placeholder to put in text.
10437 * @cfg {string} before - input group add on before
10438 * @cfg {string} after - input group add on after
10439 * @cfg {string} size - (lg|sm) or leave empty..
10440 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10441 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10442 * @cfg {Number} md colspan out of 12 for computer-sized screens
10443 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10444 * @cfg {string} value default value of the input
10445 * @cfg {Number} labelWidth set the width of label
10446 * @cfg {Number} labellg set the width of label (1-12)
10447 * @cfg {Number} labelmd set the width of label (1-12)
10448 * @cfg {Number} labelsm set the width of label (1-12)
10449 * @cfg {Number} labelxs set the width of label (1-12)
10450 * @cfg {String} labelAlign (top|left)
10451 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10452 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10453 * @cfg {String} indicatorpos (left|right) default left
10454 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10455 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10456 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10458 * @cfg {String} align (left|center|right) Default left
10459 * @cfg {Boolean} forceFeedback (true|false) Default false
10462 * Create a new Input
10463 * @param {Object} config The config object
10466 Roo.bootstrap.Input = function(config){
10468 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10473 * Fires when this field receives input focus.
10474 * @param {Roo.form.Field} this
10479 * Fires when this field loses input focus.
10480 * @param {Roo.form.Field} this
10484 * @event specialkey
10485 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10486 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10487 * @param {Roo.form.Field} this
10488 * @param {Roo.EventObject} e The event object
10493 * Fires just before the field blurs if the field value has changed.
10494 * @param {Roo.form.Field} this
10495 * @param {Mixed} newValue The new value
10496 * @param {Mixed} oldValue The original value
10501 * Fires after the field has been marked as invalid.
10502 * @param {Roo.form.Field} this
10503 * @param {String} msg The validation message
10508 * Fires after the field has been validated with no errors.
10509 * @param {Roo.form.Field} this
10514 * Fires after the key up
10515 * @param {Roo.form.Field} this
10516 * @param {Roo.EventObject} e The event Object
10521 * Fires after the user pastes into input
10522 * @param {Roo.form.Field} this
10523 * @param {Roo.EventObject} e The event Object
10529 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10531 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10532 automatic validation (defaults to "keyup").
10534 validationEvent : "keyup",
10536 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10538 validateOnBlur : true,
10540 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10542 validationDelay : 250,
10544 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10546 focusClass : "x-form-focus", // not needed???
10550 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10552 invalidClass : "has-warning",
10555 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10557 validClass : "has-success",
10560 * @cfg {Boolean} hasFeedback (true|false) default true
10562 hasFeedback : true,
10565 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10567 invalidFeedbackClass : "glyphicon-warning-sign",
10570 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10572 validFeedbackClass : "glyphicon-ok",
10575 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10577 selectOnFocus : false,
10580 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10584 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10589 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10591 disableKeyFilter : false,
10594 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10598 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10602 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10604 blankText : "Please complete this mandatory field",
10607 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10611 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10613 maxLength : Number.MAX_VALUE,
10615 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10617 minLengthText : "The minimum length for this field is {0}",
10619 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10621 maxLengthText : "The maximum length for this field is {0}",
10625 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10626 * If available, this function will be called only after the basic validators all return true, and will be passed the
10627 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10631 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10632 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10633 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10637 * @cfg {String} regexText -- Depricated - use Invalid Text
10642 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10648 autocomplete: false,
10652 inputType : 'text',
10655 placeholder: false,
10660 preventMark: false,
10661 isFormField : true,
10664 labelAlign : false,
10667 formatedValue : false,
10668 forceFeedback : false,
10670 indicatorpos : 'left',
10680 parentLabelAlign : function()
10683 while (parent.parent()) {
10684 parent = parent.parent();
10685 if (typeof(parent.labelAlign) !='undefined') {
10686 return parent.labelAlign;
10693 getAutoCreate : function()
10695 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10701 if(this.inputType != 'hidden'){
10702 cfg.cls = 'form-group' //input-group
10708 type : this.inputType,
10709 value : this.value,
10710 cls : 'form-control',
10711 placeholder : this.placeholder || '',
10712 autocomplete : this.autocomplete || 'new-password'
10714 if (this.inputType == 'file') {
10715 input.style = 'overflow:hidden'; // why not in CSS?
10718 if(this.capture.length){
10719 input.capture = this.capture;
10722 if(this.accept.length){
10723 input.accept = this.accept + "/*";
10727 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10730 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10731 input.maxLength = this.maxLength;
10734 if (this.disabled) {
10735 input.disabled=true;
10738 if (this.readOnly) {
10739 input.readonly=true;
10743 input.name = this.name;
10747 input.cls += ' input-' + this.size;
10751 ['xs','sm','md','lg'].map(function(size){
10752 if (settings[size]) {
10753 cfg.cls += ' col-' + size + '-' + settings[size];
10757 var inputblock = input;
10761 cls: 'glyphicon form-control-feedback'
10764 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10767 cls : 'has-feedback',
10775 if (this.before || this.after) {
10778 cls : 'input-group',
10782 if (this.before && typeof(this.before) == 'string') {
10784 inputblock.cn.push({
10786 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10790 if (this.before && typeof(this.before) == 'object') {
10791 this.before = Roo.factory(this.before);
10793 inputblock.cn.push({
10795 cls : 'roo-input-before input-group-prepend input-group-' +
10796 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10800 inputblock.cn.push(input);
10802 if (this.after && typeof(this.after) == 'string') {
10803 inputblock.cn.push({
10805 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10809 if (this.after && typeof(this.after) == 'object') {
10810 this.after = Roo.factory(this.after);
10812 inputblock.cn.push({
10814 cls : 'roo-input-after input-group-append input-group-' +
10815 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10819 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10820 inputblock.cls += ' has-feedback';
10821 inputblock.cn.push(feedback);
10826 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10827 tooltip : 'This field is required'
10829 if (this.allowBlank ) {
10830 indicator.style = this.allowBlank ? ' display:none' : '';
10832 if (align ==='left' && this.fieldLabel.length) {
10834 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10841 cls : 'control-label col-form-label',
10842 html : this.fieldLabel
10853 var labelCfg = cfg.cn[1];
10854 var contentCfg = cfg.cn[2];
10856 if(this.indicatorpos == 'right'){
10861 cls : 'control-label col-form-label',
10865 html : this.fieldLabel
10879 labelCfg = cfg.cn[0];
10880 contentCfg = cfg.cn[1];
10884 if(this.labelWidth > 12){
10885 labelCfg.style = "width: " + this.labelWidth + 'px';
10888 if(this.labelWidth < 13 && this.labelmd == 0){
10889 this.labelmd = this.labelWidth;
10892 if(this.labellg > 0){
10893 labelCfg.cls += ' col-lg-' + this.labellg;
10894 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10897 if(this.labelmd > 0){
10898 labelCfg.cls += ' col-md-' + this.labelmd;
10899 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10902 if(this.labelsm > 0){
10903 labelCfg.cls += ' col-sm-' + this.labelsm;
10904 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10907 if(this.labelxs > 0){
10908 labelCfg.cls += ' col-xs-' + this.labelxs;
10909 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10913 } else if ( this.fieldLabel.length) {
10920 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10921 tooltip : 'This field is required',
10922 style : this.allowBlank ? ' display:none' : ''
10926 //cls : 'input-group-addon',
10927 html : this.fieldLabel
10935 if(this.indicatorpos == 'right'){
10940 //cls : 'input-group-addon',
10941 html : this.fieldLabel
10946 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10947 tooltip : 'This field is required',
10948 style : this.allowBlank ? ' display:none' : ''
10968 if (this.parentType === 'Navbar' && this.parent().bar) {
10969 cfg.cls += ' navbar-form';
10972 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10973 // on BS4 we do this only if not form
10974 cfg.cls += ' navbar-form';
10982 * return the real input element.
10984 inputEl: function ()
10986 return this.el.select('input.form-control',true).first();
10989 tooltipEl : function()
10991 return this.inputEl();
10994 indicatorEl : function()
10996 if (Roo.bootstrap.version == 4) {
10997 return false; // not enabled in v4 yet.
11000 var indicator = this.el.select('i.roo-required-indicator',true).first();
11010 setDisabled : function(v)
11012 var i = this.inputEl().dom;
11014 i.removeAttribute('disabled');
11018 i.setAttribute('disabled','true');
11020 initEvents : function()
11023 this.inputEl().on("keydown" , this.fireKey, this);
11024 this.inputEl().on("focus", this.onFocus, this);
11025 this.inputEl().on("blur", this.onBlur, this);
11027 this.inputEl().relayEvent('keyup', this);
11028 this.inputEl().relayEvent('paste', this);
11030 this.indicator = this.indicatorEl();
11032 if(this.indicator){
11033 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11036 // reference to original value for reset
11037 this.originalValue = this.getValue();
11038 //Roo.form.TextField.superclass.initEvents.call(this);
11039 if(this.validationEvent == 'keyup'){
11040 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11041 this.inputEl().on('keyup', this.filterValidation, this);
11043 else if(this.validationEvent !== false){
11044 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11047 if(this.selectOnFocus){
11048 this.on("focus", this.preFocus, this);
11051 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11052 this.inputEl().on("keypress", this.filterKeys, this);
11054 this.inputEl().relayEvent('keypress', this);
11057 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11058 this.el.on("click", this.autoSize, this);
11061 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11062 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11065 if (typeof(this.before) == 'object') {
11066 this.before.render(this.el.select('.roo-input-before',true).first());
11068 if (typeof(this.after) == 'object') {
11069 this.after.render(this.el.select('.roo-input-after',true).first());
11072 this.inputEl().on('change', this.onChange, this);
11075 filterValidation : function(e){
11076 if(!e.isNavKeyPress()){
11077 this.validationTask.delay(this.validationDelay);
11081 * Validates the field value
11082 * @return {Boolean} True if the value is valid, else false
11084 validate : function(){
11085 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11086 if(this.disabled || this.validateValue(this.getRawValue())){
11091 this.markInvalid();
11097 * Validates a value according to the field's validation rules and marks the field as invalid
11098 * if the validation fails
11099 * @param {Mixed} value The value to validate
11100 * @return {Boolean} True if the value is valid, else false
11102 validateValue : function(value)
11104 if(this.getVisibilityEl().hasClass('hidden')){
11108 if(value.length < 1) { // if it's blank
11109 if(this.allowBlank){
11115 if(value.length < this.minLength){
11118 if(value.length > this.maxLength){
11122 var vt = Roo.form.VTypes;
11123 if(!vt[this.vtype](value, this)){
11127 if(typeof this.validator == "function"){
11128 var msg = this.validator(value);
11132 if (typeof(msg) == 'string') {
11133 this.invalidText = msg;
11137 if(this.regex && !this.regex.test(value)){
11145 fireKey : function(e){
11146 //Roo.log('field ' + e.getKey());
11147 if(e.isNavKeyPress()){
11148 this.fireEvent("specialkey", this, e);
11151 focus : function (selectText){
11153 this.inputEl().focus();
11154 if(selectText === true){
11155 this.inputEl().dom.select();
11161 onFocus : function(){
11162 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11163 // this.el.addClass(this.focusClass);
11165 if(!this.hasFocus){
11166 this.hasFocus = true;
11167 this.startValue = this.getValue();
11168 this.fireEvent("focus", this);
11172 beforeBlur : Roo.emptyFn,
11176 onBlur : function(){
11178 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11179 //this.el.removeClass(this.focusClass);
11181 this.hasFocus = false;
11182 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11185 var v = this.getValue();
11186 if(String(v) !== String(this.startValue)){
11187 this.fireEvent('change', this, v, this.startValue);
11189 this.fireEvent("blur", this);
11192 onChange : function(e)
11194 var v = this.getValue();
11195 if(String(v) !== String(this.startValue)){
11196 this.fireEvent('change', this, v, this.startValue);
11202 * Resets the current field value to the originally loaded value and clears any validation messages
11204 reset : function(){
11205 this.setValue(this.originalValue);
11209 * Returns the name of the field
11210 * @return {Mixed} name The name field
11212 getName: function(){
11216 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11217 * @return {Mixed} value The field value
11219 getValue : function(){
11221 var v = this.inputEl().getValue();
11226 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11227 * @return {Mixed} value The field value
11229 getRawValue : function(){
11230 var v = this.inputEl().getValue();
11236 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11237 * @param {Mixed} value The value to set
11239 setRawValue : function(v){
11240 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11243 selectText : function(start, end){
11244 var v = this.getRawValue();
11246 start = start === undefined ? 0 : start;
11247 end = end === undefined ? v.length : end;
11248 var d = this.inputEl().dom;
11249 if(d.setSelectionRange){
11250 d.setSelectionRange(start, end);
11251 }else if(d.createTextRange){
11252 var range = d.createTextRange();
11253 range.moveStart("character", start);
11254 range.moveEnd("character", v.length-end);
11261 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11262 * @param {Mixed} value The value to set
11264 setValue : function(v){
11267 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11273 processValue : function(value){
11274 if(this.stripCharsRe){
11275 var newValue = value.replace(this.stripCharsRe, '');
11276 if(newValue !== value){
11277 this.setRawValue(newValue);
11284 preFocus : function(){
11286 if(this.selectOnFocus){
11287 this.inputEl().dom.select();
11290 filterKeys : function(e){
11291 var k = e.getKey();
11292 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11295 var c = e.getCharCode(), cc = String.fromCharCode(c);
11296 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11299 if(!this.maskRe.test(cc)){
11304 * Clear any invalid styles/messages for this field
11306 clearInvalid : function(){
11308 if(!this.el || this.preventMark){ // not rendered
11313 this.el.removeClass([this.invalidClass, 'is-invalid']);
11315 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11317 var feedback = this.el.select('.form-control-feedback', true).first();
11320 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11325 if(this.indicator){
11326 this.indicator.removeClass('visible');
11327 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11330 this.fireEvent('valid', this);
11334 * Mark this field as valid
11336 markValid : function()
11338 if(!this.el || this.preventMark){ // not rendered...
11342 this.el.removeClass([this.invalidClass, this.validClass]);
11343 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11345 var feedback = this.el.select('.form-control-feedback', true).first();
11348 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11351 if(this.indicator){
11352 this.indicator.removeClass('visible');
11353 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11361 if(this.allowBlank && !this.getRawValue().length){
11364 if (Roo.bootstrap.version == 3) {
11365 this.el.addClass(this.validClass);
11367 this.inputEl().addClass('is-valid');
11370 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11372 var feedback = this.el.select('.form-control-feedback', true).first();
11375 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11376 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11381 this.fireEvent('valid', this);
11385 * Mark this field as invalid
11386 * @param {String} msg The validation message
11388 markInvalid : function(msg)
11390 if(!this.el || this.preventMark){ // not rendered
11394 this.el.removeClass([this.invalidClass, this.validClass]);
11395 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11397 var feedback = this.el.select('.form-control-feedback', true).first();
11400 this.el.select('.form-control-feedback', true).first().removeClass(
11401 [this.invalidFeedbackClass, this.validFeedbackClass]);
11408 if(this.allowBlank && !this.getRawValue().length){
11412 if(this.indicator){
11413 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11414 this.indicator.addClass('visible');
11416 if (Roo.bootstrap.version == 3) {
11417 this.el.addClass(this.invalidClass);
11419 this.inputEl().addClass('is-invalid');
11424 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11426 var feedback = this.el.select('.form-control-feedback', true).first();
11429 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11431 if(this.getValue().length || this.forceFeedback){
11432 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11439 this.fireEvent('invalid', this, msg);
11442 SafariOnKeyDown : function(event)
11444 // this is a workaround for a password hang bug on chrome/ webkit.
11445 if (this.inputEl().dom.type != 'password') {
11449 var isSelectAll = false;
11451 if(this.inputEl().dom.selectionEnd > 0){
11452 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11454 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11455 event.preventDefault();
11460 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11462 event.preventDefault();
11463 // this is very hacky as keydown always get's upper case.
11465 var cc = String.fromCharCode(event.getCharCode());
11466 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11470 adjustWidth : function(tag, w){
11471 tag = tag.toLowerCase();
11472 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11473 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11474 if(tag == 'input'){
11477 if(tag == 'textarea'){
11480 }else if(Roo.isOpera){
11481 if(tag == 'input'){
11484 if(tag == 'textarea'){
11492 setFieldLabel : function(v)
11494 if(!this.rendered){
11498 if(this.indicatorEl()){
11499 var ar = this.el.select('label > span',true);
11501 if (ar.elements.length) {
11502 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11503 this.fieldLabel = v;
11507 var br = this.el.select('label',true);
11509 if(br.elements.length) {
11510 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11511 this.fieldLabel = v;
11515 Roo.log('Cannot Found any of label > span || label in input');
11519 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11520 this.fieldLabel = v;
11535 * @class Roo.bootstrap.TextArea
11536 * @extends Roo.bootstrap.Input
11537 * Bootstrap TextArea class
11538 * @cfg {Number} cols Specifies the visible width of a text area
11539 * @cfg {Number} rows Specifies the visible number of lines in a text area
11540 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11541 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11542 * @cfg {string} html text
11545 * Create a new TextArea
11546 * @param {Object} config The config object
11549 Roo.bootstrap.TextArea = function(config){
11550 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11554 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11564 getAutoCreate : function(){
11566 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11572 if(this.inputType != 'hidden'){
11573 cfg.cls = 'form-group' //input-group
11581 value : this.value || '',
11582 html: this.html || '',
11583 cls : 'form-control',
11584 placeholder : this.placeholder || ''
11588 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11589 input.maxLength = this.maxLength;
11593 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11597 input.cols = this.cols;
11600 if (this.readOnly) {
11601 input.readonly = true;
11605 input.name = this.name;
11609 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11613 ['xs','sm','md','lg'].map(function(size){
11614 if (settings[size]) {
11615 cfg.cls += ' col-' + size + '-' + settings[size];
11619 var inputblock = input;
11621 if(this.hasFeedback && !this.allowBlank){
11625 cls: 'glyphicon form-control-feedback'
11629 cls : 'has-feedback',
11638 if (this.before || this.after) {
11641 cls : 'input-group',
11645 inputblock.cn.push({
11647 cls : 'input-group-addon',
11652 inputblock.cn.push(input);
11654 if(this.hasFeedback && !this.allowBlank){
11655 inputblock.cls += ' has-feedback';
11656 inputblock.cn.push(feedback);
11660 inputblock.cn.push({
11662 cls : 'input-group-addon',
11669 if (align ==='left' && this.fieldLabel.length) {
11674 cls : 'control-label',
11675 html : this.fieldLabel
11686 if(this.labelWidth > 12){
11687 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11690 if(this.labelWidth < 13 && this.labelmd == 0){
11691 this.labelmd = this.labelWidth;
11694 if(this.labellg > 0){
11695 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11696 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11699 if(this.labelmd > 0){
11700 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11701 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11704 if(this.labelsm > 0){
11705 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11706 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11709 if(this.labelxs > 0){
11710 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11711 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11714 } else if ( this.fieldLabel.length) {
11719 //cls : 'input-group-addon',
11720 html : this.fieldLabel
11738 if (this.disabled) {
11739 input.disabled=true;
11746 * return the real textarea element.
11748 inputEl: function ()
11750 return this.el.select('textarea.form-control',true).first();
11754 * Clear any invalid styles/messages for this field
11756 clearInvalid : function()
11759 if(!this.el || this.preventMark){ // not rendered
11763 var label = this.el.select('label', true).first();
11764 var icon = this.el.select('i.fa-star', true).first();
11769 this.el.removeClass( this.validClass);
11770 this.inputEl().removeClass('is-invalid');
11772 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11774 var feedback = this.el.select('.form-control-feedback', true).first();
11777 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11782 this.fireEvent('valid', this);
11786 * Mark this field as valid
11788 markValid : function()
11790 if(!this.el || this.preventMark){ // not rendered
11794 this.el.removeClass([this.invalidClass, this.validClass]);
11795 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11797 var feedback = this.el.select('.form-control-feedback', true).first();
11800 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11803 if(this.disabled || this.allowBlank){
11807 var label = this.el.select('label', true).first();
11808 var icon = this.el.select('i.fa-star', true).first();
11813 if (Roo.bootstrap.version == 3) {
11814 this.el.addClass(this.validClass);
11816 this.inputEl().addClass('is-valid');
11820 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11822 var feedback = this.el.select('.form-control-feedback', true).first();
11825 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11826 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11831 this.fireEvent('valid', this);
11835 * Mark this field as invalid
11836 * @param {String} msg The validation message
11838 markInvalid : function(msg)
11840 if(!this.el || this.preventMark){ // not rendered
11844 this.el.removeClass([this.invalidClass, this.validClass]);
11845 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11847 var feedback = this.el.select('.form-control-feedback', true).first();
11850 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11853 if(this.disabled || this.allowBlank){
11857 var label = this.el.select('label', true).first();
11858 var icon = this.el.select('i.fa-star', true).first();
11860 if(!this.getValue().length && label && !icon){
11861 this.el.createChild({
11863 cls : 'text-danger fa fa-lg fa-star',
11864 tooltip : 'This field is required',
11865 style : 'margin-right:5px;'
11869 if (Roo.bootstrap.version == 3) {
11870 this.el.addClass(this.invalidClass);
11872 this.inputEl().addClass('is-invalid');
11875 // fixme ... this may be depricated need to test..
11876 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11878 var feedback = this.el.select('.form-control-feedback', true).first();
11881 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11883 if(this.getValue().length || this.forceFeedback){
11884 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11891 this.fireEvent('invalid', this, msg);
11899 * trigger field - base class for combo..
11904 * @class Roo.bootstrap.TriggerField
11905 * @extends Roo.bootstrap.Input
11906 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11907 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11908 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11909 * for which you can provide a custom implementation. For example:
11911 var trigger = new Roo.bootstrap.TriggerField();
11912 trigger.onTriggerClick = myTriggerFn;
11913 trigger.applyTo('my-field');
11916 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11917 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11918 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11919 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11920 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11923 * Create a new TriggerField.
11924 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11925 * to the base TextField)
11927 Roo.bootstrap.TriggerField = function(config){
11928 this.mimicing = false;
11929 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11932 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11934 * @cfg {String} triggerClass A CSS class to apply to the trigger
11937 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11942 * @cfg {Boolean} removable (true|false) special filter default false
11946 /** @cfg {Boolean} grow @hide */
11947 /** @cfg {Number} growMin @hide */
11948 /** @cfg {Number} growMax @hide */
11954 autoSize: Roo.emptyFn,
11958 deferHeight : true,
11961 actionMode : 'wrap',
11966 getAutoCreate : function(){
11968 var align = this.labelAlign || this.parentLabelAlign();
11973 cls: 'form-group' //input-group
11980 type : this.inputType,
11981 cls : 'form-control',
11982 autocomplete: 'new-password',
11983 placeholder : this.placeholder || ''
11987 input.name = this.name;
11990 input.cls += ' input-' + this.size;
11993 if (this.disabled) {
11994 input.disabled=true;
11997 var inputblock = input;
11999 if(this.hasFeedback && !this.allowBlank){
12003 cls: 'glyphicon form-control-feedback'
12006 if(this.removable && !this.editable ){
12008 cls : 'has-feedback',
12014 cls : 'roo-combo-removable-btn close'
12021 cls : 'has-feedback',
12030 if(this.removable && !this.editable ){
12032 cls : 'roo-removable',
12038 cls : 'roo-combo-removable-btn close'
12045 if (this.before || this.after) {
12048 cls : 'input-group',
12052 inputblock.cn.push({
12054 cls : 'input-group-addon input-group-prepend input-group-text',
12059 inputblock.cn.push(input);
12061 if(this.hasFeedback && !this.allowBlank){
12062 inputblock.cls += ' has-feedback';
12063 inputblock.cn.push(feedback);
12067 inputblock.cn.push({
12069 cls : 'input-group-addon input-group-append input-group-text',
12078 var ibwrap = inputblock;
12083 cls: 'roo-select2-choices',
12087 cls: 'roo-select2-search-field',
12099 cls: 'roo-select2-container input-group',
12104 cls: 'form-hidden-field'
12110 if(!this.multiple && this.showToggleBtn){
12116 if (this.caret != false) {
12119 cls: 'fa fa-' + this.caret
12126 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12128 Roo.bootstrap.version == 3 ? caret : '',
12131 cls: 'combobox-clear',
12145 combobox.cls += ' roo-select2-container-multi';
12149 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12150 tooltip : 'This field is required'
12152 if (Roo.bootstrap.version == 4) {
12155 style : 'display:none'
12160 if (align ==='left' && this.fieldLabel.length) {
12162 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12169 cls : 'control-label',
12170 html : this.fieldLabel
12182 var labelCfg = cfg.cn[1];
12183 var contentCfg = cfg.cn[2];
12185 if(this.indicatorpos == 'right'){
12190 cls : 'control-label',
12194 html : this.fieldLabel
12208 labelCfg = cfg.cn[0];
12209 contentCfg = cfg.cn[1];
12212 if(this.labelWidth > 12){
12213 labelCfg.style = "width: " + this.labelWidth + 'px';
12216 if(this.labelWidth < 13 && this.labelmd == 0){
12217 this.labelmd = this.labelWidth;
12220 if(this.labellg > 0){
12221 labelCfg.cls += ' col-lg-' + this.labellg;
12222 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12225 if(this.labelmd > 0){
12226 labelCfg.cls += ' col-md-' + this.labelmd;
12227 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12230 if(this.labelsm > 0){
12231 labelCfg.cls += ' col-sm-' + this.labelsm;
12232 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12235 if(this.labelxs > 0){
12236 labelCfg.cls += ' col-xs-' + this.labelxs;
12237 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12240 } else if ( this.fieldLabel.length) {
12241 // Roo.log(" label");
12246 //cls : 'input-group-addon',
12247 html : this.fieldLabel
12255 if(this.indicatorpos == 'right'){
12263 html : this.fieldLabel
12277 // Roo.log(" no label && no align");
12284 ['xs','sm','md','lg'].map(function(size){
12285 if (settings[size]) {
12286 cfg.cls += ' col-' + size + '-' + settings[size];
12297 onResize : function(w, h){
12298 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12299 // if(typeof w == 'number'){
12300 // var x = w - this.trigger.getWidth();
12301 // this.inputEl().setWidth(this.adjustWidth('input', x));
12302 // this.trigger.setStyle('left', x+'px');
12307 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12310 getResizeEl : function(){
12311 return this.inputEl();
12315 getPositionEl : function(){
12316 return this.inputEl();
12320 alignErrorIcon : function(){
12321 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12325 initEvents : function(){
12329 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12330 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12331 if(!this.multiple && this.showToggleBtn){
12332 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12333 if(this.hideTrigger){
12334 this.trigger.setDisplayed(false);
12336 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12340 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12343 if(this.removable && !this.editable && !this.tickable){
12344 var close = this.closeTriggerEl();
12347 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12348 close.on('click', this.removeBtnClick, this, close);
12352 //this.trigger.addClassOnOver('x-form-trigger-over');
12353 //this.trigger.addClassOnClick('x-form-trigger-click');
12356 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12360 closeTriggerEl : function()
12362 var close = this.el.select('.roo-combo-removable-btn', true).first();
12363 return close ? close : false;
12366 removeBtnClick : function(e, h, el)
12368 e.preventDefault();
12370 if(this.fireEvent("remove", this) !== false){
12372 this.fireEvent("afterremove", this)
12376 createList : function()
12378 this.list = Roo.get(document.body).createChild({
12379 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12380 cls: 'typeahead typeahead-long dropdown-menu shadow',
12381 style: 'display:none'
12384 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12389 initTrigger : function(){
12394 onDestroy : function(){
12396 this.trigger.removeAllListeners();
12397 // this.trigger.remove();
12400 // this.wrap.remove();
12402 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12406 onFocus : function(){
12407 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12409 if(!this.mimicing){
12410 this.wrap.addClass('x-trigger-wrap-focus');
12411 this.mimicing = true;
12412 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12413 if(this.monitorTab){
12414 this.el.on("keydown", this.checkTab, this);
12421 checkTab : function(e){
12422 if(e.getKey() == e.TAB){
12423 this.triggerBlur();
12428 onBlur : function(){
12433 mimicBlur : function(e, t){
12435 if(!this.wrap.contains(t) && this.validateBlur()){
12436 this.triggerBlur();
12442 triggerBlur : function(){
12443 this.mimicing = false;
12444 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12445 if(this.monitorTab){
12446 this.el.un("keydown", this.checkTab, this);
12448 //this.wrap.removeClass('x-trigger-wrap-focus');
12449 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12453 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12454 validateBlur : function(e, t){
12459 onDisable : function(){
12460 this.inputEl().dom.disabled = true;
12461 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12463 // this.wrap.addClass('x-item-disabled');
12468 onEnable : function(){
12469 this.inputEl().dom.disabled = false;
12470 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12472 // this.el.removeClass('x-item-disabled');
12477 onShow : function(){
12478 var ae = this.getActionEl();
12481 ae.dom.style.display = '';
12482 ae.dom.style.visibility = 'visible';
12488 onHide : function(){
12489 var ae = this.getActionEl();
12490 ae.dom.style.display = 'none';
12494 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12495 * by an implementing function.
12497 * @param {EventObject} e
12499 onTriggerClick : Roo.emptyFn
12507 * @class Roo.bootstrap.CardUploader
12508 * @extends Roo.bootstrap.Button
12509 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12510 * @cfg {Number} errorTimeout default 3000
12511 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12512 * @cfg {Array} html The button text.
12516 * Create a new CardUploader
12517 * @param {Object} config The config object
12520 Roo.bootstrap.CardUploader = function(config){
12524 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12527 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12535 * When a image is clicked on - and needs to display a slideshow or similar..
12536 * @param {Roo.bootstrap.Card} this
12537 * @param {Object} The image information data
12543 * When a the download link is clicked
12544 * @param {Roo.bootstrap.Card} this
12545 * @param {Object} The image information data contains
12552 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12555 errorTimeout : 3000,
12559 fileCollection : false,
12562 getAutoCreate : function()
12566 cls :'form-group' ,
12571 //cls : 'input-group-addon',
12572 html : this.fieldLabel
12580 value : this.value,
12581 cls : 'd-none form-control'
12586 multiple : 'multiple',
12588 cls : 'd-none roo-card-upload-selector'
12592 cls : 'roo-card-uploader-button-container w-100 mb-2'
12595 cls : 'card-columns roo-card-uploader-container'
12605 getChildContainer : function() /// what children are added to.
12607 return this.containerEl;
12610 getButtonContainer : function() /// what children are added to.
12612 return this.el.select(".roo-card-uploader-button-container").first();
12615 initEvents : function()
12618 Roo.bootstrap.Input.prototype.initEvents.call(this);
12622 xns: Roo.bootstrap,
12625 container_method : 'getButtonContainer' ,
12626 html : this.html, // fix changable?
12629 'click' : function(btn, e) {
12638 this.urlAPI = (window.createObjectURL && window) ||
12639 (window.URL && URL.revokeObjectURL && URL) ||
12640 (window.webkitURL && webkitURL);
12645 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12647 this.selectorEl.on('change', this.onFileSelected, this);
12650 this.images.forEach(function(img) {
12653 this.images = false;
12655 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12661 onClick : function(e)
12663 e.preventDefault();
12665 this.selectorEl.dom.click();
12669 onFileSelected : function(e)
12671 e.preventDefault();
12673 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12677 Roo.each(this.selectorEl.dom.files, function(file){
12678 this.addFile(file);
12687 addFile : function(file)
12690 if(typeof(file) === 'string'){
12691 throw "Add file by name?"; // should not happen
12695 if(!file || !this.urlAPI){
12705 var url = _this.urlAPI.createObjectURL( file);
12708 id : Roo.bootstrap.CardUploader.ID--,
12709 is_uploaded : false,
12713 mimetype : file.type,
12721 * addCard - add an Attachment to the uploader
12722 * @param data - the data about the image to upload
12726 title : "Title of file",
12727 is_uploaded : false,
12728 src : "http://.....",
12729 srcfile : { the File upload object },
12730 mimetype : file.type,
12733 .. any other data...
12739 addCard : function (data)
12741 // hidden input element?
12742 // if the file is not an image...
12743 //then we need to use something other that and header_image
12748 xns : Roo.bootstrap,
12749 xtype : 'CardFooter',
12752 xns : Roo.bootstrap,
12758 xns : Roo.bootstrap,
12760 html : String.format("<small>{0}</small>", data.title),
12761 cls : 'col-10 text-left',
12766 click : function() {
12768 t.fireEvent( "download", t, data );
12774 xns : Roo.bootstrap,
12776 style: 'max-height: 28px; ',
12782 click : function() {
12783 t.removeCard(data.id)
12795 var cn = this.addxtype(
12798 xns : Roo.bootstrap,
12801 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12802 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12803 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12808 initEvents : function() {
12809 Roo.bootstrap.Card.prototype.initEvents.call(this);
12811 this.imgEl = this.el.select('.card-img-top').first();
12813 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12814 this.imgEl.set({ 'pointer' : 'cursor' });
12817 this.getCardFooter().addClass('p-1');
12824 // dont' really need ot update items.
12825 // this.items.push(cn);
12826 this.fileCollection.add(cn);
12828 if (!data.srcfile) {
12829 this.updateInput();
12834 var reader = new FileReader();
12835 reader.addEventListener("load", function() {
12836 data.srcdata = reader.result;
12839 reader.readAsDataURL(data.srcfile);
12844 removeCard : function(id)
12847 var card = this.fileCollection.get(id);
12848 card.data.is_deleted = 1;
12849 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12850 //this.fileCollection.remove(card);
12851 //this.items = this.items.filter(function(e) { return e != card });
12852 // dont' really need ot update items.
12853 card.el.dom.parentNode.removeChild(card.el.dom);
12854 this.updateInput();
12860 this.fileCollection.each(function(card) {
12861 if (card.el.dom && card.el.dom.parentNode) {
12862 card.el.dom.parentNode.removeChild(card.el.dom);
12865 this.fileCollection.clear();
12866 this.updateInput();
12869 updateInput : function()
12872 this.fileCollection.each(function(e) {
12876 this.inputEl().dom.value = JSON.stringify(data);
12886 Roo.bootstrap.CardUploader.ID = -1;/*
12888 * Ext JS Library 1.1.1
12889 * Copyright(c) 2006-2007, Ext JS, LLC.
12891 * Originally Released Under LGPL - original licence link has changed is not relivant.
12894 * <script type="text/javascript">
12899 * @class Roo.data.SortTypes
12901 * Defines the default sorting (casting?) comparison functions used when sorting data.
12903 Roo.data.SortTypes = {
12905 * Default sort that does nothing
12906 * @param {Mixed} s The value being converted
12907 * @return {Mixed} The comparison value
12909 none : function(s){
12914 * The regular expression used to strip tags
12918 stripTagsRE : /<\/?[^>]+>/gi,
12921 * Strips all HTML tags to sort on text only
12922 * @param {Mixed} s The value being converted
12923 * @return {String} The comparison value
12925 asText : function(s){
12926 return String(s).replace(this.stripTagsRE, "");
12930 * Strips all HTML tags to sort on text only - Case insensitive
12931 * @param {Mixed} s The value being converted
12932 * @return {String} The comparison value
12934 asUCText : function(s){
12935 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12939 * Case insensitive string
12940 * @param {Mixed} s The value being converted
12941 * @return {String} The comparison value
12943 asUCString : function(s) {
12944 return String(s).toUpperCase();
12949 * @param {Mixed} s The value being converted
12950 * @return {Number} The comparison value
12952 asDate : function(s) {
12956 if(s instanceof Date){
12957 return s.getTime();
12959 return Date.parse(String(s));
12964 * @param {Mixed} s The value being converted
12965 * @return {Float} The comparison value
12967 asFloat : function(s) {
12968 var val = parseFloat(String(s).replace(/,/g, ""));
12977 * @param {Mixed} s The value being converted
12978 * @return {Number} The comparison value
12980 asInt : function(s) {
12981 var val = parseInt(String(s).replace(/,/g, ""));
12989 * Ext JS Library 1.1.1
12990 * Copyright(c) 2006-2007, Ext JS, LLC.
12992 * Originally Released Under LGPL - original licence link has changed is not relivant.
12995 * <script type="text/javascript">
12999 * @class Roo.data.Record
13000 * Instances of this class encapsulate both record <em>definition</em> information, and record
13001 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13002 * to access Records cached in an {@link Roo.data.Store} object.<br>
13004 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13005 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13008 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13010 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13011 * {@link #create}. The parameters are the same.
13012 * @param {Array} data An associative Array of data values keyed by the field name.
13013 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13014 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13015 * not specified an integer id is generated.
13017 Roo.data.Record = function(data, id){
13018 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13023 * Generate a constructor for a specific record layout.
13024 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13025 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13026 * Each field definition object may contain the following properties: <ul>
13027 * <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,
13028 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13029 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13030 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13031 * is being used, then this is a string containing the javascript expression to reference the data relative to
13032 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13033 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13034 * this may be omitted.</p></li>
13035 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13036 * <ul><li>auto (Default, implies no conversion)</li>
13041 * <li>date</li></ul></p></li>
13042 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13043 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13044 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13045 * by the Reader into an object that will be stored in the Record. It is passed the
13046 * following parameters:<ul>
13047 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13049 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13051 * <br>usage:<br><pre><code>
13052 var TopicRecord = Roo.data.Record.create(
13053 {name: 'title', mapping: 'topic_title'},
13054 {name: 'author', mapping: 'username'},
13055 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13056 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13057 {name: 'lastPoster', mapping: 'user2'},
13058 {name: 'excerpt', mapping: 'post_text'}
13061 var myNewRecord = new TopicRecord({
13062 title: 'Do my job please',
13065 lastPost: new Date(),
13066 lastPoster: 'Animal',
13067 excerpt: 'No way dude!'
13069 myStore.add(myNewRecord);
13074 Roo.data.Record.create = function(o){
13075 var f = function(){
13076 f.superclass.constructor.apply(this, arguments);
13078 Roo.extend(f, Roo.data.Record);
13079 var p = f.prototype;
13080 p.fields = new Roo.util.MixedCollection(false, function(field){
13083 for(var i = 0, len = o.length; i < len; i++){
13084 p.fields.add(new Roo.data.Field(o[i]));
13086 f.getField = function(name){
13087 return p.fields.get(name);
13092 Roo.data.Record.AUTO_ID = 1000;
13093 Roo.data.Record.EDIT = 'edit';
13094 Roo.data.Record.REJECT = 'reject';
13095 Roo.data.Record.COMMIT = 'commit';
13097 Roo.data.Record.prototype = {
13099 * Readonly flag - true if this record has been modified.
13108 join : function(store){
13109 this.store = store;
13113 * Set the named field to the specified value.
13114 * @param {String} name The name of the field to set.
13115 * @param {Object} value The value to set the field to.
13117 set : function(name, value){
13118 if(this.data[name] == value){
13122 if(!this.modified){
13123 this.modified = {};
13125 if(typeof this.modified[name] == 'undefined'){
13126 this.modified[name] = this.data[name];
13128 this.data[name] = value;
13129 if(!this.editing && this.store){
13130 this.store.afterEdit(this);
13135 * Get the value of the named field.
13136 * @param {String} name The name of the field to get the value of.
13137 * @return {Object} The value of the field.
13139 get : function(name){
13140 return this.data[name];
13144 beginEdit : function(){
13145 this.editing = true;
13146 this.modified = {};
13150 cancelEdit : function(){
13151 this.editing = false;
13152 delete this.modified;
13156 endEdit : function(){
13157 this.editing = false;
13158 if(this.dirty && this.store){
13159 this.store.afterEdit(this);
13164 * Usually called by the {@link Roo.data.Store} which owns the Record.
13165 * Rejects all changes made to the Record since either creation, or the last commit operation.
13166 * Modified fields are reverted to their original values.
13168 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13169 * of reject operations.
13171 reject : function(){
13172 var m = this.modified;
13174 if(typeof m[n] != "function"){
13175 this.data[n] = m[n];
13178 this.dirty = false;
13179 delete this.modified;
13180 this.editing = false;
13182 this.store.afterReject(this);
13187 * Usually called by the {@link Roo.data.Store} which owns the Record.
13188 * Commits all changes made to the Record since either creation, or the last commit operation.
13190 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13191 * of commit operations.
13193 commit : function(){
13194 this.dirty = false;
13195 delete this.modified;
13196 this.editing = false;
13198 this.store.afterCommit(this);
13203 hasError : function(){
13204 return this.error != null;
13208 clearError : function(){
13213 * Creates a copy of this record.
13214 * @param {String} id (optional) A new record id if you don't want to use this record's id
13217 copy : function(newId) {
13218 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13222 * Ext JS Library 1.1.1
13223 * Copyright(c) 2006-2007, Ext JS, LLC.
13225 * Originally Released Under LGPL - original licence link has changed is not relivant.
13228 * <script type="text/javascript">
13234 * @class Roo.data.Store
13235 * @extends Roo.util.Observable
13236 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13237 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13239 * 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
13240 * has no knowledge of the format of the data returned by the Proxy.<br>
13242 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13243 * instances from the data object. These records are cached and made available through accessor functions.
13245 * Creates a new Store.
13246 * @param {Object} config A config object containing the objects needed for the Store to access data,
13247 * and read the data into Records.
13249 Roo.data.Store = function(config){
13250 this.data = new Roo.util.MixedCollection(false);
13251 this.data.getKey = function(o){
13254 this.baseParams = {};
13256 this.paramNames = {
13261 "multisort" : "_multisort"
13264 if(config && config.data){
13265 this.inlineData = config.data;
13266 delete config.data;
13269 Roo.apply(this, config);
13271 if(this.reader){ // reader passed
13272 this.reader = Roo.factory(this.reader, Roo.data);
13273 this.reader.xmodule = this.xmodule || false;
13274 if(!this.recordType){
13275 this.recordType = this.reader.recordType;
13277 if(this.reader.onMetaChange){
13278 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13282 if(this.recordType){
13283 this.fields = this.recordType.prototype.fields;
13285 this.modified = [];
13289 * @event datachanged
13290 * Fires when the data cache has changed, and a widget which is using this Store
13291 * as a Record cache should refresh its view.
13292 * @param {Store} this
13294 datachanged : true,
13296 * @event metachange
13297 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13298 * @param {Store} this
13299 * @param {Object} meta The JSON metadata
13304 * Fires when Records have been added to the Store
13305 * @param {Store} this
13306 * @param {Roo.data.Record[]} records The array of Records added
13307 * @param {Number} index The index at which the record(s) were added
13312 * Fires when a Record has been removed from the Store
13313 * @param {Store} this
13314 * @param {Roo.data.Record} record The Record that was removed
13315 * @param {Number} index The index at which the record was removed
13320 * Fires when a Record has been updated
13321 * @param {Store} this
13322 * @param {Roo.data.Record} record The Record that was updated
13323 * @param {String} operation The update operation being performed. Value may be one of:
13325 Roo.data.Record.EDIT
13326 Roo.data.Record.REJECT
13327 Roo.data.Record.COMMIT
13333 * Fires when the data cache has been cleared.
13334 * @param {Store} this
13338 * @event beforeload
13339 * Fires before a request is made for a new data object. If the beforeload handler returns false
13340 * the load action will be canceled.
13341 * @param {Store} this
13342 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13346 * @event beforeloadadd
13347 * Fires after a new set of Records has been loaded.
13348 * @param {Store} this
13349 * @param {Roo.data.Record[]} records The Records that were loaded
13350 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13352 beforeloadadd : true,
13355 * Fires after a new set of Records has been loaded, before they are added to the store.
13356 * @param {Store} this
13357 * @param {Roo.data.Record[]} records The Records that were loaded
13358 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13359 * @params {Object} return from reader
13363 * @event loadexception
13364 * Fires if an exception occurs in the Proxy during loading.
13365 * Called with the signature of the Proxy's "loadexception" event.
13366 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13369 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13370 * @param {Object} load options
13371 * @param {Object} jsonData from your request (normally this contains the Exception)
13373 loadexception : true
13377 this.proxy = Roo.factory(this.proxy, Roo.data);
13378 this.proxy.xmodule = this.xmodule || false;
13379 this.relayEvents(this.proxy, ["loadexception"]);
13381 this.sortToggle = {};
13382 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13384 Roo.data.Store.superclass.constructor.call(this);
13386 if(this.inlineData){
13387 this.loadData(this.inlineData);
13388 delete this.inlineData;
13392 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13394 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13395 * without a remote query - used by combo/forms at present.
13399 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13402 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13405 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13406 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13409 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13410 * on any HTTP request
13413 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13416 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13420 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13421 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13423 remoteSort : false,
13426 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13427 * loaded or when a record is removed. (defaults to false).
13429 pruneModifiedRecords : false,
13432 lastOptions : null,
13435 * Add Records to the Store and fires the add event.
13436 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13438 add : function(records){
13439 records = [].concat(records);
13440 for(var i = 0, len = records.length; i < len; i++){
13441 records[i].join(this);
13443 var index = this.data.length;
13444 this.data.addAll(records);
13445 this.fireEvent("add", this, records, index);
13449 * Remove a Record from the Store and fires the remove event.
13450 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13452 remove : function(record){
13453 var index = this.data.indexOf(record);
13454 this.data.removeAt(index);
13456 if(this.pruneModifiedRecords){
13457 this.modified.remove(record);
13459 this.fireEvent("remove", this, record, index);
13463 * Remove all Records from the Store and fires the clear event.
13465 removeAll : function(){
13467 if(this.pruneModifiedRecords){
13468 this.modified = [];
13470 this.fireEvent("clear", this);
13474 * Inserts Records to the Store at the given index and fires the add event.
13475 * @param {Number} index The start index at which to insert the passed Records.
13476 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13478 insert : function(index, records){
13479 records = [].concat(records);
13480 for(var i = 0, len = records.length; i < len; i++){
13481 this.data.insert(index, records[i]);
13482 records[i].join(this);
13484 this.fireEvent("add", this, records, index);
13488 * Get the index within the cache of the passed Record.
13489 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13490 * @return {Number} The index of the passed Record. Returns -1 if not found.
13492 indexOf : function(record){
13493 return this.data.indexOf(record);
13497 * Get the index within the cache of the Record with the passed id.
13498 * @param {String} id The id of the Record to find.
13499 * @return {Number} The index of the Record. Returns -1 if not found.
13501 indexOfId : function(id){
13502 return this.data.indexOfKey(id);
13506 * Get the Record with the specified id.
13507 * @param {String} id The id of the Record to find.
13508 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13510 getById : function(id){
13511 return this.data.key(id);
13515 * Get the Record at the specified index.
13516 * @param {Number} index The index of the Record to find.
13517 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13519 getAt : function(index){
13520 return this.data.itemAt(index);
13524 * Returns a range of Records between specified indices.
13525 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13526 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13527 * @return {Roo.data.Record[]} An array of Records
13529 getRange : function(start, end){
13530 return this.data.getRange(start, end);
13534 storeOptions : function(o){
13535 o = Roo.apply({}, o);
13538 this.lastOptions = o;
13542 * Loads the Record cache from the configured Proxy using the configured Reader.
13544 * If using remote paging, then the first load call must specify the <em>start</em>
13545 * and <em>limit</em> properties in the options.params property to establish the initial
13546 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13548 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13549 * and this call will return before the new data has been loaded. Perform any post-processing
13550 * in a callback function, or in a "load" event handler.</strong>
13552 * @param {Object} options An object containing properties which control loading options:<ul>
13553 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13554 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13555 * passed the following arguments:<ul>
13556 * <li>r : Roo.data.Record[]</li>
13557 * <li>options: Options object from the load call</li>
13558 * <li>success: Boolean success indicator</li></ul></li>
13559 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13560 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13563 load : function(options){
13564 options = options || {};
13565 if(this.fireEvent("beforeload", this, options) !== false){
13566 this.storeOptions(options);
13567 var p = Roo.apply(options.params || {}, this.baseParams);
13568 // if meta was not loaded from remote source.. try requesting it.
13569 if (!this.reader.metaFromRemote) {
13570 p._requestMeta = 1;
13572 if(this.sortInfo && this.remoteSort){
13573 var pn = this.paramNames;
13574 p[pn["sort"]] = this.sortInfo.field;
13575 p[pn["dir"]] = this.sortInfo.direction;
13577 if (this.multiSort) {
13578 var pn = this.paramNames;
13579 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13582 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13587 * Reloads the Record cache from the configured Proxy using the configured Reader and
13588 * the options from the last load operation performed.
13589 * @param {Object} options (optional) An object containing properties which may override the options
13590 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13591 * the most recently used options are reused).
13593 reload : function(options){
13594 this.load(Roo.applyIf(options||{}, this.lastOptions));
13598 // Called as a callback by the Reader during a load operation.
13599 loadRecords : function(o, options, success){
13600 if(!o || success === false){
13601 if(success !== false){
13602 this.fireEvent("load", this, [], options, o);
13604 if(options.callback){
13605 options.callback.call(options.scope || this, [], options, false);
13609 // if data returned failure - throw an exception.
13610 if (o.success === false) {
13611 // show a message if no listener is registered.
13612 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13613 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13615 // loadmask wil be hooked into this..
13616 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13619 var r = o.records, t = o.totalRecords || r.length;
13621 this.fireEvent("beforeloadadd", this, r, options, o);
13623 if(!options || options.add !== true){
13624 if(this.pruneModifiedRecords){
13625 this.modified = [];
13627 for(var i = 0, len = r.length; i < len; i++){
13631 this.data = this.snapshot;
13632 delete this.snapshot;
13635 this.data.addAll(r);
13636 this.totalLength = t;
13638 this.fireEvent("datachanged", this);
13640 this.totalLength = Math.max(t, this.data.length+r.length);
13644 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13646 var e = new Roo.data.Record({});
13648 e.set(this.parent.displayField, this.parent.emptyTitle);
13649 e.set(this.parent.valueField, '');
13654 this.fireEvent("load", this, r, options, o);
13655 if(options.callback){
13656 options.callback.call(options.scope || this, r, options, true);
13662 * Loads data from a passed data block. A Reader which understands the format of the data
13663 * must have been configured in the constructor.
13664 * @param {Object} data The data block from which to read the Records. The format of the data expected
13665 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13666 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13668 loadData : function(o, append){
13669 var r = this.reader.readRecords(o);
13670 this.loadRecords(r, {add: append}, true);
13674 * using 'cn' the nested child reader read the child array into it's child stores.
13675 * @param {Object} rec The record with a 'children array
13677 loadDataFromChildren : function(rec)
13679 this.loadData(this.reader.toLoadData(rec));
13684 * Gets the number of cached records.
13686 * <em>If using paging, this may not be the total size of the dataset. If the data object
13687 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13688 * the data set size</em>
13690 getCount : function(){
13691 return this.data.length || 0;
13695 * Gets the total number of records in the dataset as returned by the server.
13697 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13698 * the dataset size</em>
13700 getTotalCount : function(){
13701 return this.totalLength || 0;
13705 * Returns the sort state of the Store as an object with two properties:
13707 field {String} The name of the field by which the Records are sorted
13708 direction {String} The sort order, "ASC" or "DESC"
13711 getSortState : function(){
13712 return this.sortInfo;
13716 applySort : function(){
13717 if(this.sortInfo && !this.remoteSort){
13718 var s = this.sortInfo, f = s.field;
13719 var st = this.fields.get(f).sortType;
13720 var fn = function(r1, r2){
13721 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13722 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13724 this.data.sort(s.direction, fn);
13725 if(this.snapshot && this.snapshot != this.data){
13726 this.snapshot.sort(s.direction, fn);
13732 * Sets the default sort column and order to be used by the next load operation.
13733 * @param {String} fieldName The name of the field to sort by.
13734 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13736 setDefaultSort : function(field, dir){
13737 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13741 * Sort the Records.
13742 * If remote sorting is used, the sort is performed on the server, and the cache is
13743 * reloaded. If local sorting is used, the cache is sorted internally.
13744 * @param {String} fieldName The name of the field to sort by.
13745 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13747 sort : function(fieldName, dir){
13748 var f = this.fields.get(fieldName);
13750 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13752 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13753 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13758 this.sortToggle[f.name] = dir;
13759 this.sortInfo = {field: f.name, direction: dir};
13760 if(!this.remoteSort){
13762 this.fireEvent("datachanged", this);
13764 this.load(this.lastOptions);
13769 * Calls the specified function for each of the Records in the cache.
13770 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13771 * Returning <em>false</em> aborts and exits the iteration.
13772 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13774 each : function(fn, scope){
13775 this.data.each(fn, scope);
13779 * Gets all records modified since the last commit. Modified records are persisted across load operations
13780 * (e.g., during paging).
13781 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13783 getModifiedRecords : function(){
13784 return this.modified;
13788 createFilterFn : function(property, value, anyMatch){
13789 if(!value.exec){ // not a regex
13790 value = String(value);
13791 if(value.length == 0){
13794 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13796 return function(r){
13797 return value.test(r.data[property]);
13802 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13803 * @param {String} property A field on your records
13804 * @param {Number} start The record index to start at (defaults to 0)
13805 * @param {Number} end The last record index to include (defaults to length - 1)
13806 * @return {Number} The sum
13808 sum : function(property, start, end){
13809 var rs = this.data.items, v = 0;
13810 start = start || 0;
13811 end = (end || end === 0) ? end : rs.length-1;
13813 for(var i = start; i <= end; i++){
13814 v += (rs[i].data[property] || 0);
13820 * Filter the records by a specified property.
13821 * @param {String} field A field on your records
13822 * @param {String/RegExp} value Either a string that the field
13823 * should start with or a RegExp to test against the field
13824 * @param {Boolean} anyMatch True to match any part not just the beginning
13826 filter : function(property, value, anyMatch){
13827 var fn = this.createFilterFn(property, value, anyMatch);
13828 return fn ? this.filterBy(fn) : this.clearFilter();
13832 * Filter by a function. The specified function will be called with each
13833 * record in this data source. If the function returns true the record is included,
13834 * otherwise it is filtered.
13835 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13836 * @param {Object} scope (optional) The scope of the function (defaults to this)
13838 filterBy : function(fn, scope){
13839 this.snapshot = this.snapshot || this.data;
13840 this.data = this.queryBy(fn, scope||this);
13841 this.fireEvent("datachanged", this);
13845 * Query the records by a specified property.
13846 * @param {String} field A field on your records
13847 * @param {String/RegExp} value Either a string that the field
13848 * should start with or a RegExp to test against the field
13849 * @param {Boolean} anyMatch True to match any part not just the beginning
13850 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13852 query : function(property, value, anyMatch){
13853 var fn = this.createFilterFn(property, value, anyMatch);
13854 return fn ? this.queryBy(fn) : this.data.clone();
13858 * Query by a function. The specified function will be called with each
13859 * record in this data source. If the function returns true the record is included
13861 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13862 * @param {Object} scope (optional) The scope of the function (defaults to this)
13863 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13865 queryBy : function(fn, scope){
13866 var data = this.snapshot || this.data;
13867 return data.filterBy(fn, scope||this);
13871 * Collects unique values for a particular dataIndex from this store.
13872 * @param {String} dataIndex The property to collect
13873 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13874 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13875 * @return {Array} An array of the unique values
13877 collect : function(dataIndex, allowNull, bypassFilter){
13878 var d = (bypassFilter === true && this.snapshot) ?
13879 this.snapshot.items : this.data.items;
13880 var v, sv, r = [], l = {};
13881 for(var i = 0, len = d.length; i < len; i++){
13882 v = d[i].data[dataIndex];
13884 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13893 * Revert to a view of the Record cache with no filtering applied.
13894 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13896 clearFilter : function(suppressEvent){
13897 if(this.snapshot && this.snapshot != this.data){
13898 this.data = this.snapshot;
13899 delete this.snapshot;
13900 if(suppressEvent !== true){
13901 this.fireEvent("datachanged", this);
13907 afterEdit : function(record){
13908 if(this.modified.indexOf(record) == -1){
13909 this.modified.push(record);
13911 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13915 afterReject : function(record){
13916 this.modified.remove(record);
13917 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13921 afterCommit : function(record){
13922 this.modified.remove(record);
13923 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13927 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13928 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13930 commitChanges : function(){
13931 var m = this.modified.slice(0);
13932 this.modified = [];
13933 for(var i = 0, len = m.length; i < len; i++){
13939 * Cancel outstanding changes on all changed records.
13941 rejectChanges : function(){
13942 var m = this.modified.slice(0);
13943 this.modified = [];
13944 for(var i = 0, len = m.length; i < len; i++){
13949 onMetaChange : function(meta, rtype, o){
13950 this.recordType = rtype;
13951 this.fields = rtype.prototype.fields;
13952 delete this.snapshot;
13953 this.sortInfo = meta.sortInfo || this.sortInfo;
13954 this.modified = [];
13955 this.fireEvent('metachange', this, this.reader.meta);
13958 moveIndex : function(data, type)
13960 var index = this.indexOf(data);
13962 var newIndex = index + type;
13966 this.insert(newIndex, data);
13971 * Ext JS Library 1.1.1
13972 * Copyright(c) 2006-2007, Ext JS, LLC.
13974 * Originally Released Under LGPL - original licence link has changed is not relivant.
13977 * <script type="text/javascript">
13981 * @class Roo.data.SimpleStore
13982 * @extends Roo.data.Store
13983 * Small helper class to make creating Stores from Array data easier.
13984 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13985 * @cfg {Array} fields An array of field definition objects, or field name strings.
13986 * @cfg {Object} an existing reader (eg. copied from another store)
13987 * @cfg {Array} data The multi-dimensional array of data
13989 * @param {Object} config
13991 Roo.data.SimpleStore = function(config)
13993 Roo.data.SimpleStore.superclass.constructor.call(this, {
13995 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13998 Roo.data.Record.create(config.fields)
14000 proxy : new Roo.data.MemoryProxy(config.data)
14004 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14006 * Ext JS Library 1.1.1
14007 * Copyright(c) 2006-2007, Ext JS, LLC.
14009 * Originally Released Under LGPL - original licence link has changed is not relivant.
14012 * <script type="text/javascript">
14017 * @extends Roo.data.Store
14018 * @class Roo.data.JsonStore
14019 * Small helper class to make creating Stores for JSON data easier. <br/>
14021 var store = new Roo.data.JsonStore({
14022 url: 'get-images.php',
14024 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14027 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14028 * JsonReader and HttpProxy (unless inline data is provided).</b>
14029 * @cfg {Array} fields An array of field definition objects, or field name strings.
14031 * @param {Object} config
14033 Roo.data.JsonStore = function(c){
14034 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14035 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14036 reader: new Roo.data.JsonReader(c, c.fields)
14039 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14041 * Ext JS Library 1.1.1
14042 * Copyright(c) 2006-2007, Ext JS, LLC.
14044 * Originally Released Under LGPL - original licence link has changed is not relivant.
14047 * <script type="text/javascript">
14051 Roo.data.Field = function(config){
14052 if(typeof config == "string"){
14053 config = {name: config};
14055 Roo.apply(this, config);
14058 this.type = "auto";
14061 var st = Roo.data.SortTypes;
14062 // named sortTypes are supported, here we look them up
14063 if(typeof this.sortType == "string"){
14064 this.sortType = st[this.sortType];
14067 // set default sortType for strings and dates
14068 if(!this.sortType){
14071 this.sortType = st.asUCString;
14074 this.sortType = st.asDate;
14077 this.sortType = st.none;
14082 var stripRe = /[\$,%]/g;
14084 // prebuilt conversion function for this field, instead of
14085 // switching every time we're reading a value
14087 var cv, dateFormat = this.dateFormat;
14092 cv = function(v){ return v; };
14095 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14099 return v !== undefined && v !== null && v !== '' ?
14100 parseInt(String(v).replace(stripRe, ""), 10) : '';
14105 return v !== undefined && v !== null && v !== '' ?
14106 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14111 cv = function(v){ return v === true || v === "true" || v == 1; };
14118 if(v instanceof Date){
14122 if(dateFormat == "timestamp"){
14123 return new Date(v*1000);
14125 return Date.parseDate(v, dateFormat);
14127 var parsed = Date.parse(v);
14128 return parsed ? new Date(parsed) : null;
14137 Roo.data.Field.prototype = {
14145 * Ext JS Library 1.1.1
14146 * Copyright(c) 2006-2007, Ext JS, LLC.
14148 * Originally Released Under LGPL - original licence link has changed is not relivant.
14151 * <script type="text/javascript">
14154 // Base class for reading structured data from a data source. This class is intended to be
14155 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14158 * @class Roo.data.DataReader
14159 * Base class for reading structured data from a data source. This class is intended to be
14160 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14163 Roo.data.DataReader = function(meta, recordType){
14167 this.recordType = recordType instanceof Array ?
14168 Roo.data.Record.create(recordType) : recordType;
14171 Roo.data.DataReader.prototype = {
14174 readerType : 'Data',
14176 * Create an empty record
14177 * @param {Object} data (optional) - overlay some values
14178 * @return {Roo.data.Record} record created.
14180 newRow : function(d) {
14182 this.recordType.prototype.fields.each(function(c) {
14184 case 'int' : da[c.name] = 0; break;
14185 case 'date' : da[c.name] = new Date(); break;
14186 case 'float' : da[c.name] = 0.0; break;
14187 case 'boolean' : da[c.name] = false; break;
14188 default : da[c.name] = ""; break;
14192 return new this.recordType(Roo.apply(da, d));
14198 * Ext JS Library 1.1.1
14199 * Copyright(c) 2006-2007, Ext JS, LLC.
14201 * Originally Released Under LGPL - original licence link has changed is not relivant.
14204 * <script type="text/javascript">
14208 * @class Roo.data.DataProxy
14209 * @extends Roo.data.Observable
14210 * This class is an abstract base class for implementations which provide retrieval of
14211 * unformatted data objects.<br>
14213 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14214 * (of the appropriate type which knows how to parse the data object) to provide a block of
14215 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14217 * Custom implementations must implement the load method as described in
14218 * {@link Roo.data.HttpProxy#load}.
14220 Roo.data.DataProxy = function(){
14223 * @event beforeload
14224 * Fires before a network request is made to retrieve a data object.
14225 * @param {Object} This DataProxy object.
14226 * @param {Object} params The params parameter to the load function.
14231 * Fires before the load method's callback is called.
14232 * @param {Object} This DataProxy object.
14233 * @param {Object} o The data object.
14234 * @param {Object} arg The callback argument object passed to the load function.
14238 * @event loadexception
14239 * Fires if an Exception occurs during data retrieval.
14240 * @param {Object} This DataProxy object.
14241 * @param {Object} o The data object.
14242 * @param {Object} arg The callback argument object passed to the load function.
14243 * @param {Object} e The Exception.
14245 loadexception : true
14247 Roo.data.DataProxy.superclass.constructor.call(this);
14250 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14253 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14257 * Ext JS Library 1.1.1
14258 * Copyright(c) 2006-2007, Ext JS, LLC.
14260 * Originally Released Under LGPL - original licence link has changed is not relivant.
14263 * <script type="text/javascript">
14266 * @class Roo.data.MemoryProxy
14267 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14268 * to the Reader when its load method is called.
14270 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14272 Roo.data.MemoryProxy = function(data){
14276 Roo.data.MemoryProxy.superclass.constructor.call(this);
14280 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14283 * Load data from the requested source (in this case an in-memory
14284 * data object passed to the constructor), read the data object into
14285 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14286 * process that block using the passed callback.
14287 * @param {Object} params This parameter is not used by the MemoryProxy class.
14288 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14289 * object into a block of Roo.data.Records.
14290 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14291 * The function must be passed <ul>
14292 * <li>The Record block object</li>
14293 * <li>The "arg" argument from the load function</li>
14294 * <li>A boolean success indicator</li>
14296 * @param {Object} scope The scope in which to call the callback
14297 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14299 load : function(params, reader, callback, scope, arg){
14300 params = params || {};
14303 result = reader.readRecords(params.data ? params.data :this.data);
14305 this.fireEvent("loadexception", this, arg, null, e);
14306 callback.call(scope, null, arg, false);
14309 callback.call(scope, result, arg, true);
14313 update : function(params, records){
14318 * Ext JS Library 1.1.1
14319 * Copyright(c) 2006-2007, Ext JS, LLC.
14321 * Originally Released Under LGPL - original licence link has changed is not relivant.
14324 * <script type="text/javascript">
14327 * @class Roo.data.HttpProxy
14328 * @extends Roo.data.DataProxy
14329 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14330 * configured to reference a certain URL.<br><br>
14332 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14333 * from which the running page was served.<br><br>
14335 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14337 * Be aware that to enable the browser to parse an XML document, the server must set
14338 * the Content-Type header in the HTTP response to "text/xml".
14340 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14341 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14342 * will be used to make the request.
14344 Roo.data.HttpProxy = function(conn){
14345 Roo.data.HttpProxy.superclass.constructor.call(this);
14346 // is conn a conn config or a real conn?
14348 this.useAjax = !conn || !conn.events;
14352 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14353 // thse are take from connection...
14356 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14359 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14360 * extra parameters to each request made by this object. (defaults to undefined)
14363 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14364 * to each request made by this object. (defaults to undefined)
14367 * @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)
14370 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14373 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14379 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14383 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14384 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14385 * a finer-grained basis than the DataProxy events.
14387 getConnection : function(){
14388 return this.useAjax ? Roo.Ajax : this.conn;
14392 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14393 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14394 * process that block using the passed callback.
14395 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14396 * for the request to the remote server.
14397 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14398 * object into a block of Roo.data.Records.
14399 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14400 * The function must be passed <ul>
14401 * <li>The Record block object</li>
14402 * <li>The "arg" argument from the load function</li>
14403 * <li>A boolean success indicator</li>
14405 * @param {Object} scope The scope in which to call the callback
14406 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14408 load : function(params, reader, callback, scope, arg){
14409 if(this.fireEvent("beforeload", this, params) !== false){
14411 params : params || {},
14413 callback : callback,
14418 callback : this.loadResponse,
14422 Roo.applyIf(o, this.conn);
14423 if(this.activeRequest){
14424 Roo.Ajax.abort(this.activeRequest);
14426 this.activeRequest = Roo.Ajax.request(o);
14428 this.conn.request(o);
14431 callback.call(scope||this, null, arg, false);
14436 loadResponse : function(o, success, response){
14437 delete this.activeRequest;
14439 this.fireEvent("loadexception", this, o, response);
14440 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14445 result = o.reader.read(response);
14447 this.fireEvent("loadexception", this, o, response, e);
14448 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14452 this.fireEvent("load", this, o, o.request.arg);
14453 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14457 update : function(dataSet){
14462 updateResponse : function(dataSet){
14467 * Ext JS Library 1.1.1
14468 * Copyright(c) 2006-2007, Ext JS, LLC.
14470 * Originally Released Under LGPL - original licence link has changed is not relivant.
14473 * <script type="text/javascript">
14477 * @class Roo.data.ScriptTagProxy
14478 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14479 * other than the originating domain of the running page.<br><br>
14481 * <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
14482 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14484 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14485 * source code that is used as the source inside a <script> tag.<br><br>
14487 * In order for the browser to process the returned data, the server must wrap the data object
14488 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14489 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14490 * depending on whether the callback name was passed:
14493 boolean scriptTag = false;
14494 String cb = request.getParameter("callback");
14497 response.setContentType("text/javascript");
14499 response.setContentType("application/x-json");
14501 Writer out = response.getWriter();
14503 out.write(cb + "(");
14505 out.print(dataBlock.toJsonString());
14512 * @param {Object} config A configuration object.
14514 Roo.data.ScriptTagProxy = function(config){
14515 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14516 Roo.apply(this, config);
14517 this.head = document.getElementsByTagName("head")[0];
14520 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14522 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14524 * @cfg {String} url The URL from which to request the data object.
14527 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14531 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14532 * the server the name of the callback function set up by the load call to process the returned data object.
14533 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14534 * javascript output which calls this named function passing the data object as its only parameter.
14536 callbackParam : "callback",
14538 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14539 * name to the request.
14544 * Load data from the configured URL, read the data object into
14545 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14546 * process that block using the passed callback.
14547 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14548 * for the request to the remote server.
14549 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14550 * object into a block of Roo.data.Records.
14551 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14552 * The function must be passed <ul>
14553 * <li>The Record block object</li>
14554 * <li>The "arg" argument from the load function</li>
14555 * <li>A boolean success indicator</li>
14557 * @param {Object} scope The scope in which to call the callback
14558 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14560 load : function(params, reader, callback, scope, arg){
14561 if(this.fireEvent("beforeload", this, params) !== false){
14563 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14565 var url = this.url;
14566 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14568 url += "&_dc=" + (new Date().getTime());
14570 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14573 cb : "stcCallback"+transId,
14574 scriptId : "stcScript"+transId,
14578 callback : callback,
14584 window[trans.cb] = function(o){
14585 conn.handleResponse(o, trans);
14588 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14590 if(this.autoAbort !== false){
14594 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14596 var script = document.createElement("script");
14597 script.setAttribute("src", url);
14598 script.setAttribute("type", "text/javascript");
14599 script.setAttribute("id", trans.scriptId);
14600 this.head.appendChild(script);
14602 this.trans = trans;
14604 callback.call(scope||this, null, arg, false);
14609 isLoading : function(){
14610 return this.trans ? true : false;
14614 * Abort the current server request.
14616 abort : function(){
14617 if(this.isLoading()){
14618 this.destroyTrans(this.trans);
14623 destroyTrans : function(trans, isLoaded){
14624 this.head.removeChild(document.getElementById(trans.scriptId));
14625 clearTimeout(trans.timeoutId);
14627 window[trans.cb] = undefined;
14629 delete window[trans.cb];
14632 // if hasn't been loaded, wait for load to remove it to prevent script error
14633 window[trans.cb] = function(){
14634 window[trans.cb] = undefined;
14636 delete window[trans.cb];
14643 handleResponse : function(o, trans){
14644 this.trans = false;
14645 this.destroyTrans(trans, true);
14648 result = trans.reader.readRecords(o);
14650 this.fireEvent("loadexception", this, o, trans.arg, e);
14651 trans.callback.call(trans.scope||window, null, trans.arg, false);
14654 this.fireEvent("load", this, o, trans.arg);
14655 trans.callback.call(trans.scope||window, result, trans.arg, true);
14659 handleFailure : function(trans){
14660 this.trans = false;
14661 this.destroyTrans(trans, false);
14662 this.fireEvent("loadexception", this, null, trans.arg);
14663 trans.callback.call(trans.scope||window, null, trans.arg, false);
14667 * Ext JS Library 1.1.1
14668 * Copyright(c) 2006-2007, Ext JS, LLC.
14670 * Originally Released Under LGPL - original licence link has changed is not relivant.
14673 * <script type="text/javascript">
14677 * @class Roo.data.JsonReader
14678 * @extends Roo.data.DataReader
14679 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14680 * based on mappings in a provided Roo.data.Record constructor.
14682 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14683 * in the reply previously.
14688 var RecordDef = Roo.data.Record.create([
14689 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14690 {name: 'occupation'} // This field will use "occupation" as the mapping.
14692 var myReader = new Roo.data.JsonReader({
14693 totalProperty: "results", // The property which contains the total dataset size (optional)
14694 root: "rows", // The property which contains an Array of row objects
14695 id: "id" // The property within each row object that provides an ID for the record (optional)
14699 * This would consume a JSON file like this:
14701 { 'results': 2, 'rows': [
14702 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14703 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14706 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14707 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14708 * paged from the remote server.
14709 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14710 * @cfg {String} root name of the property which contains the Array of row objects.
14711 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14712 * @cfg {Array} fields Array of field definition objects
14714 * Create a new JsonReader
14715 * @param {Object} meta Metadata configuration options
14716 * @param {Object} recordType Either an Array of field definition objects,
14717 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14719 Roo.data.JsonReader = function(meta, recordType){
14722 // set some defaults:
14723 Roo.applyIf(meta, {
14724 totalProperty: 'total',
14725 successProperty : 'success',
14730 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14732 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14734 readerType : 'Json',
14737 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14738 * Used by Store query builder to append _requestMeta to params.
14741 metaFromRemote : false,
14743 * This method is only used by a DataProxy which has retrieved data from a remote server.
14744 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14745 * @return {Object} data A data block which is used by an Roo.data.Store object as
14746 * a cache of Roo.data.Records.
14748 read : function(response){
14749 var json = response.responseText;
14751 var o = /* eval:var:o */ eval("("+json+")");
14753 throw {message: "JsonReader.read: Json object not found"};
14759 this.metaFromRemote = true;
14760 this.meta = o.metaData;
14761 this.recordType = Roo.data.Record.create(o.metaData.fields);
14762 this.onMetaChange(this.meta, this.recordType, o);
14764 return this.readRecords(o);
14767 // private function a store will implement
14768 onMetaChange : function(meta, recordType, o){
14775 simpleAccess: function(obj, subsc) {
14782 getJsonAccessor: function(){
14784 return function(expr) {
14786 return(re.test(expr))
14787 ? new Function("obj", "return obj." + expr)
14792 return Roo.emptyFn;
14797 * Create a data block containing Roo.data.Records from an XML document.
14798 * @param {Object} o An object which contains an Array of row objects in the property specified
14799 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14800 * which contains the total size of the dataset.
14801 * @return {Object} data A data block which is used by an Roo.data.Store object as
14802 * a cache of Roo.data.Records.
14804 readRecords : function(o){
14806 * After any data loads, the raw JSON data is available for further custom processing.
14810 var s = this.meta, Record = this.recordType,
14811 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14813 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14815 if(s.totalProperty) {
14816 this.getTotal = this.getJsonAccessor(s.totalProperty);
14818 if(s.successProperty) {
14819 this.getSuccess = this.getJsonAccessor(s.successProperty);
14821 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14823 var g = this.getJsonAccessor(s.id);
14824 this.getId = function(rec) {
14826 return (r === undefined || r === "") ? null : r;
14829 this.getId = function(){return null;};
14832 for(var jj = 0; jj < fl; jj++){
14834 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14835 this.ef[jj] = this.getJsonAccessor(map);
14839 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14840 if(s.totalProperty){
14841 var vt = parseInt(this.getTotal(o), 10);
14846 if(s.successProperty){
14847 var vs = this.getSuccess(o);
14848 if(vs === false || vs === 'false'){
14853 for(var i = 0; i < c; i++){
14856 var id = this.getId(n);
14857 for(var j = 0; j < fl; j++){
14859 var v = this.ef[j](n);
14861 Roo.log('missing convert for ' + f.name);
14865 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14867 var record = new Record(values, id);
14869 records[i] = record;
14875 totalRecords : totalRecords
14878 // used when loading children.. @see loadDataFromChildren
14879 toLoadData: function(rec)
14881 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14882 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14883 return { data : data, total : data.length };
14888 * Ext JS Library 1.1.1
14889 * Copyright(c) 2006-2007, Ext JS, LLC.
14891 * Originally Released Under LGPL - original licence link has changed is not relivant.
14894 * <script type="text/javascript">
14898 * @class Roo.data.ArrayReader
14899 * @extends Roo.data.DataReader
14900 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14901 * Each element of that Array represents a row of data fields. The
14902 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14903 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14907 var RecordDef = Roo.data.Record.create([
14908 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14909 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14911 var myReader = new Roo.data.ArrayReader({
14912 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14916 * This would consume an Array like this:
14918 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14922 * Create a new JsonReader
14923 * @param {Object} meta Metadata configuration options.
14924 * @param {Object|Array} recordType Either an Array of field definition objects
14926 * @cfg {Array} fields Array of field definition objects
14927 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14928 * as specified to {@link Roo.data.Record#create},
14929 * or an {@link Roo.data.Record} object
14932 * created using {@link Roo.data.Record#create}.
14934 Roo.data.ArrayReader = function(meta, recordType)
14936 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14939 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14942 * Create a data block containing Roo.data.Records from an XML document.
14943 * @param {Object} o An Array of row objects which represents the dataset.
14944 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14945 * a cache of Roo.data.Records.
14947 readRecords : function(o)
14949 var sid = this.meta ? this.meta.id : null;
14950 var recordType = this.recordType, fields = recordType.prototype.fields;
14953 for(var i = 0; i < root.length; i++){
14956 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14957 for(var j = 0, jlen = fields.length; j < jlen; j++){
14958 var f = fields.items[j];
14959 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14960 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14962 values[f.name] = v;
14964 var record = new recordType(values, id);
14966 records[records.length] = record;
14970 totalRecords : records.length
14973 // used when loading children.. @see loadDataFromChildren
14974 toLoadData: function(rec)
14976 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14977 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14988 * @class Roo.bootstrap.ComboBox
14989 * @extends Roo.bootstrap.TriggerField
14990 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14991 * @cfg {Boolean} append (true|false) default false
14992 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14993 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14994 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14995 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14996 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14997 * @cfg {Boolean} animate default true
14998 * @cfg {Boolean} emptyResultText only for touch device
14999 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15000 * @cfg {String} emptyTitle default ''
15001 * @cfg {Number} width fixed with? experimental
15003 * Create a new ComboBox.
15004 * @param {Object} config Configuration options
15006 Roo.bootstrap.ComboBox = function(config){
15007 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15011 * Fires when the dropdown list is expanded
15012 * @param {Roo.bootstrap.ComboBox} combo This combo box
15017 * Fires when the dropdown list is collapsed
15018 * @param {Roo.bootstrap.ComboBox} combo This combo box
15022 * @event beforeselect
15023 * Fires before a list item is selected. Return false to cancel the selection.
15024 * @param {Roo.bootstrap.ComboBox} combo This combo box
15025 * @param {Roo.data.Record} record The data record returned from the underlying store
15026 * @param {Number} index The index of the selected item in the dropdown list
15028 'beforeselect' : true,
15031 * Fires when a list item is selected
15032 * @param {Roo.bootstrap.ComboBox} combo This combo box
15033 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15034 * @param {Number} index The index of the selected item in the dropdown list
15038 * @event beforequery
15039 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15040 * The event object passed has these properties:
15041 * @param {Roo.bootstrap.ComboBox} combo This combo box
15042 * @param {String} query The query
15043 * @param {Boolean} forceAll true to force "all" query
15044 * @param {Boolean} cancel true to cancel the query
15045 * @param {Object} e The query event object
15047 'beforequery': true,
15050 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15051 * @param {Roo.bootstrap.ComboBox} combo This combo box
15056 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15057 * @param {Roo.bootstrap.ComboBox} combo This combo box
15058 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15063 * Fires when the remove value from the combobox array
15064 * @param {Roo.bootstrap.ComboBox} combo This combo box
15068 * @event afterremove
15069 * Fires when the remove value from the combobox array
15070 * @param {Roo.bootstrap.ComboBox} combo This combo box
15072 'afterremove' : true,
15074 * @event specialfilter
15075 * Fires when specialfilter
15076 * @param {Roo.bootstrap.ComboBox} combo This combo box
15078 'specialfilter' : true,
15081 * Fires when tick the element
15082 * @param {Roo.bootstrap.ComboBox} combo This combo box
15086 * @event touchviewdisplay
15087 * Fires when touch view require special display (default is using displayField)
15088 * @param {Roo.bootstrap.ComboBox} combo This combo box
15089 * @param {Object} cfg set html .
15091 'touchviewdisplay' : true
15096 this.tickItems = [];
15098 this.selectedIndex = -1;
15099 if(this.mode == 'local'){
15100 if(config.queryDelay === undefined){
15101 this.queryDelay = 10;
15103 if(config.minChars === undefined){
15109 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15112 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15113 * rendering into an Roo.Editor, defaults to false)
15116 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15117 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15120 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15123 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15124 * the dropdown list (defaults to undefined, with no header element)
15128 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15132 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15134 listWidth: undefined,
15136 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15137 * mode = 'remote' or 'text' if mode = 'local')
15139 displayField: undefined,
15142 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15143 * mode = 'remote' or 'value' if mode = 'local').
15144 * Note: use of a valueField requires the user make a selection
15145 * in order for a value to be mapped.
15147 valueField: undefined,
15149 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15154 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15155 * field's data value (defaults to the underlying DOM element's name)
15157 hiddenName: undefined,
15159 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15163 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15165 selectedClass: 'active',
15168 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15172 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15173 * anchor positions (defaults to 'tl-bl')
15175 listAlign: 'tl-bl?',
15177 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15181 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15182 * query specified by the allQuery config option (defaults to 'query')
15184 triggerAction: 'query',
15186 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15187 * (defaults to 4, does not apply if editable = false)
15191 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15192 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15196 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15197 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15201 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15202 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15206 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15207 * when editable = true (defaults to false)
15209 selectOnFocus:false,
15211 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15213 queryParam: 'query',
15215 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15216 * when mode = 'remote' (defaults to 'Loading...')
15218 loadingText: 'Loading...',
15220 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15224 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15228 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15229 * traditional select (defaults to true)
15233 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15237 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15241 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15242 * listWidth has a higher value)
15246 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15247 * allow the user to set arbitrary text into the field (defaults to false)
15249 forceSelection:false,
15251 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15252 * if typeAhead = true (defaults to 250)
15254 typeAheadDelay : 250,
15256 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15257 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15259 valueNotFoundText : undefined,
15261 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15263 blockFocus : false,
15266 * @cfg {Boolean} disableClear Disable showing of clear button.
15268 disableClear : false,
15270 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15272 alwaysQuery : false,
15275 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15280 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15282 invalidClass : "has-warning",
15285 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15287 validClass : "has-success",
15290 * @cfg {Boolean} specialFilter (true|false) special filter default false
15292 specialFilter : false,
15295 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15297 mobileTouchView : true,
15300 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15302 useNativeIOS : false,
15305 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15307 mobile_restrict_height : false,
15309 ios_options : false,
15321 btnPosition : 'right',
15322 triggerList : true,
15323 showToggleBtn : true,
15325 emptyResultText: 'Empty',
15326 triggerText : 'Select',
15330 // element that contains real text value.. (when hidden is used..)
15332 getAutoCreate : function()
15337 * Render classic select for iso
15340 if(Roo.isIOS && this.useNativeIOS){
15341 cfg = this.getAutoCreateNativeIOS();
15349 if(Roo.isTouch && this.mobileTouchView){
15350 cfg = this.getAutoCreateTouchView();
15357 if(!this.tickable){
15358 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15363 * ComboBox with tickable selections
15366 var align = this.labelAlign || this.parentLabelAlign();
15369 cls : 'form-group roo-combobox-tickable' //input-group
15372 var btn_text_select = '';
15373 var btn_text_done = '';
15374 var btn_text_cancel = '';
15376 if (this.btn_text_show) {
15377 btn_text_select = 'Select';
15378 btn_text_done = 'Done';
15379 btn_text_cancel = 'Cancel';
15384 cls : 'tickable-buttons',
15389 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15390 //html : this.triggerText
15391 html: btn_text_select
15397 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15399 html: btn_text_done
15405 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15407 html: btn_text_cancel
15413 buttons.cn.unshift({
15415 cls: 'roo-select2-search-field-input'
15421 Roo.each(buttons.cn, function(c){
15423 c.cls += ' btn-' + _this.size;
15426 if (_this.disabled) {
15433 style : 'display: contents',
15438 cls: 'form-hidden-field'
15442 cls: 'roo-select2-choices',
15446 cls: 'roo-select2-search-field',
15457 cls: 'roo-select2-container input-group roo-select2-container-multi',
15463 // cls: 'typeahead typeahead-long dropdown-menu',
15464 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15469 if(this.hasFeedback && !this.allowBlank){
15473 cls: 'glyphicon form-control-feedback'
15476 combobox.cn.push(feedback);
15483 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15484 tooltip : 'This field is required'
15486 if (Roo.bootstrap.version == 4) {
15489 style : 'display:none'
15492 if (align ==='left' && this.fieldLabel.length) {
15494 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15501 cls : 'control-label col-form-label',
15502 html : this.fieldLabel
15514 var labelCfg = cfg.cn[1];
15515 var contentCfg = cfg.cn[2];
15518 if(this.indicatorpos == 'right'){
15524 cls : 'control-label col-form-label',
15528 html : this.fieldLabel
15544 labelCfg = cfg.cn[0];
15545 contentCfg = cfg.cn[1];
15549 if(this.labelWidth > 12){
15550 labelCfg.style = "width: " + this.labelWidth + 'px';
15552 if(this.width * 1 > 0){
15553 contentCfg.style = "width: " + this.width + 'px';
15555 if(this.labelWidth < 13 && this.labelmd == 0){
15556 this.labelmd = this.labelWidth;
15559 if(this.labellg > 0){
15560 labelCfg.cls += ' col-lg-' + this.labellg;
15561 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15564 if(this.labelmd > 0){
15565 labelCfg.cls += ' col-md-' + this.labelmd;
15566 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15569 if(this.labelsm > 0){
15570 labelCfg.cls += ' col-sm-' + this.labelsm;
15571 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15574 if(this.labelxs > 0){
15575 labelCfg.cls += ' col-xs-' + this.labelxs;
15576 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15580 } else if ( this.fieldLabel.length) {
15581 // Roo.log(" label");
15586 //cls : 'input-group-addon',
15587 html : this.fieldLabel
15592 if(this.indicatorpos == 'right'){
15596 //cls : 'input-group-addon',
15597 html : this.fieldLabel
15607 // Roo.log(" no label && no align");
15614 ['xs','sm','md','lg'].map(function(size){
15615 if (settings[size]) {
15616 cfg.cls += ' col-' + size + '-' + settings[size];
15624 _initEventsCalled : false,
15627 initEvents: function()
15629 if (this._initEventsCalled) { // as we call render... prevent looping...
15632 this._initEventsCalled = true;
15635 throw "can not find store for combo";
15638 this.indicator = this.indicatorEl();
15640 this.store = Roo.factory(this.store, Roo.data);
15641 this.store.parent = this;
15643 // if we are building from html. then this element is so complex, that we can not really
15644 // use the rendered HTML.
15645 // so we have to trash and replace the previous code.
15646 if (Roo.XComponent.build_from_html) {
15647 // remove this element....
15648 var e = this.el.dom, k=0;
15649 while (e ) { e = e.previousSibling; ++k;}
15654 this.rendered = false;
15656 this.render(this.parent().getChildContainer(true), k);
15659 if(Roo.isIOS && this.useNativeIOS){
15660 this.initIOSView();
15668 if(Roo.isTouch && this.mobileTouchView){
15669 this.initTouchView();
15674 this.initTickableEvents();
15678 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15680 if(this.hiddenName){
15682 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15684 this.hiddenField.dom.value =
15685 this.hiddenValue !== undefined ? this.hiddenValue :
15686 this.value !== undefined ? this.value : '';
15688 // prevent input submission
15689 this.el.dom.removeAttribute('name');
15690 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15695 // this.el.dom.setAttribute('autocomplete', 'off');
15698 var cls = 'x-combo-list';
15700 //this.list = new Roo.Layer({
15701 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15707 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15708 _this.list.setWidth(lw);
15711 this.list.on('mouseover', this.onViewOver, this);
15712 this.list.on('mousemove', this.onViewMove, this);
15713 this.list.on('scroll', this.onViewScroll, this);
15716 this.list.swallowEvent('mousewheel');
15717 this.assetHeight = 0;
15720 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15721 this.assetHeight += this.header.getHeight();
15724 this.innerList = this.list.createChild({cls:cls+'-inner'});
15725 this.innerList.on('mouseover', this.onViewOver, this);
15726 this.innerList.on('mousemove', this.onViewMove, this);
15727 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15729 if(this.allowBlank && !this.pageSize && !this.disableClear){
15730 this.footer = this.list.createChild({cls:cls+'-ft'});
15731 this.pageTb = new Roo.Toolbar(this.footer);
15735 this.footer = this.list.createChild({cls:cls+'-ft'});
15736 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15737 {pageSize: this.pageSize});
15741 if (this.pageTb && this.allowBlank && !this.disableClear) {
15743 this.pageTb.add(new Roo.Toolbar.Fill(), {
15744 cls: 'x-btn-icon x-btn-clear',
15746 handler: function()
15749 _this.clearValue();
15750 _this.onSelect(false, -1);
15755 this.assetHeight += this.footer.getHeight();
15760 this.tpl = Roo.bootstrap.version == 4 ?
15761 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15762 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15765 this.view = new Roo.View(this.list, this.tpl, {
15766 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15768 //this.view.wrapEl.setDisplayed(false);
15769 this.view.on('click', this.onViewClick, this);
15772 this.store.on('beforeload', this.onBeforeLoad, this);
15773 this.store.on('load', this.onLoad, this);
15774 this.store.on('loadexception', this.onLoadException, this);
15776 if(this.resizable){
15777 this.resizer = new Roo.Resizable(this.list, {
15778 pinned:true, handles:'se'
15780 this.resizer.on('resize', function(r, w, h){
15781 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15782 this.listWidth = w;
15783 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15784 this.restrictHeight();
15786 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15789 if(!this.editable){
15790 this.editable = true;
15791 this.setEditable(false);
15796 if (typeof(this.events.add.listeners) != 'undefined') {
15798 this.addicon = this.wrap.createChild(
15799 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15801 this.addicon.on('click', function(e) {
15802 this.fireEvent('add', this);
15805 if (typeof(this.events.edit.listeners) != 'undefined') {
15807 this.editicon = this.wrap.createChild(
15808 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15809 if (this.addicon) {
15810 this.editicon.setStyle('margin-left', '40px');
15812 this.editicon.on('click', function(e) {
15814 // we fire even if inothing is selected..
15815 this.fireEvent('edit', this, this.lastData );
15821 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15822 "up" : function(e){
15823 this.inKeyMode = true;
15827 "down" : function(e){
15828 if(!this.isExpanded()){
15829 this.onTriggerClick();
15831 this.inKeyMode = true;
15836 "enter" : function(e){
15837 // this.onViewClick();
15841 if(this.fireEvent("specialkey", this, e)){
15842 this.onViewClick(false);
15848 "esc" : function(e){
15852 "tab" : function(e){
15855 if(this.fireEvent("specialkey", this, e)){
15856 this.onViewClick(false);
15864 doRelay : function(foo, bar, hname){
15865 if(hname == 'down' || this.scope.isExpanded()){
15866 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15875 this.queryDelay = Math.max(this.queryDelay || 10,
15876 this.mode == 'local' ? 10 : 250);
15879 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15881 if(this.typeAhead){
15882 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15884 if(this.editable !== false){
15885 this.inputEl().on("keyup", this.onKeyUp, this);
15887 if(this.forceSelection){
15888 this.inputEl().on('blur', this.doForce, this);
15892 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15893 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15897 initTickableEvents: function()
15901 if(this.hiddenName){
15903 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15905 this.hiddenField.dom.value =
15906 this.hiddenValue !== undefined ? this.hiddenValue :
15907 this.value !== undefined ? this.value : '';
15909 // prevent input submission
15910 this.el.dom.removeAttribute('name');
15911 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15916 // this.list = this.el.select('ul.dropdown-menu',true).first();
15918 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15919 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15920 if(this.triggerList){
15921 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15924 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15925 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15927 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15928 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15930 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15931 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15933 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15934 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15935 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15938 this.cancelBtn.hide();
15943 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15944 _this.list.setWidth(lw);
15947 this.list.on('mouseover', this.onViewOver, this);
15948 this.list.on('mousemove', this.onViewMove, this);
15950 this.list.on('scroll', this.onViewScroll, this);
15953 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15954 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15957 this.view = new Roo.View(this.list, this.tpl, {
15962 selectedClass: this.selectedClass
15965 //this.view.wrapEl.setDisplayed(false);
15966 this.view.on('click', this.onViewClick, this);
15970 this.store.on('beforeload', this.onBeforeLoad, this);
15971 this.store.on('load', this.onLoad, this);
15972 this.store.on('loadexception', this.onLoadException, this);
15975 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15976 "up" : function(e){
15977 this.inKeyMode = true;
15981 "down" : function(e){
15982 this.inKeyMode = true;
15986 "enter" : function(e){
15987 if(this.fireEvent("specialkey", this, e)){
15988 this.onViewClick(false);
15994 "esc" : function(e){
15995 this.onTickableFooterButtonClick(e, false, false);
15998 "tab" : function(e){
15999 this.fireEvent("specialkey", this, e);
16001 this.onTickableFooterButtonClick(e, false, false);
16008 doRelay : function(e, fn, key){
16009 if(this.scope.isExpanded()){
16010 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16019 this.queryDelay = Math.max(this.queryDelay || 10,
16020 this.mode == 'local' ? 10 : 250);
16023 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16025 if(this.typeAhead){
16026 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16029 if(this.editable !== false){
16030 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16033 this.indicator = this.indicatorEl();
16035 if(this.indicator){
16036 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16037 this.indicator.hide();
16042 onDestroy : function(){
16044 this.view.setStore(null);
16045 this.view.el.removeAllListeners();
16046 this.view.el.remove();
16047 this.view.purgeListeners();
16050 this.list.dom.innerHTML = '';
16054 this.store.un('beforeload', this.onBeforeLoad, this);
16055 this.store.un('load', this.onLoad, this);
16056 this.store.un('loadexception', this.onLoadException, this);
16058 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16062 fireKey : function(e){
16063 if(e.isNavKeyPress() && !this.list.isVisible()){
16064 this.fireEvent("specialkey", this, e);
16069 onResize: function(w, h)
16073 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16075 // if(typeof w != 'number'){
16076 // // we do not handle it!?!?
16079 // var tw = this.trigger.getWidth();
16080 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16081 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16083 // this.inputEl().setWidth( this.adjustWidth('input', x));
16085 // //this.trigger.setStyle('left', x+'px');
16087 // if(this.list && this.listWidth === undefined){
16088 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16089 // this.list.setWidth(lw);
16090 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16098 * Allow or prevent the user from directly editing the field text. If false is passed,
16099 * the user will only be able to select from the items defined in the dropdown list. This method
16100 * is the runtime equivalent of setting the 'editable' config option at config time.
16101 * @param {Boolean} value True to allow the user to directly edit the field text
16103 setEditable : function(value){
16104 if(value == this.editable){
16107 this.editable = value;
16109 this.inputEl().dom.setAttribute('readOnly', true);
16110 this.inputEl().on('mousedown', this.onTriggerClick, this);
16111 this.inputEl().addClass('x-combo-noedit');
16113 this.inputEl().dom.setAttribute('readOnly', false);
16114 this.inputEl().un('mousedown', this.onTriggerClick, this);
16115 this.inputEl().removeClass('x-combo-noedit');
16121 onBeforeLoad : function(combo,opts){
16122 if(!this.hasFocus){
16126 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16128 this.restrictHeight();
16129 this.selectedIndex = -1;
16133 onLoad : function(){
16135 this.hasQuery = false;
16137 if(!this.hasFocus){
16141 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16142 this.loading.hide();
16145 if(this.store.getCount() > 0){
16148 this.restrictHeight();
16149 if(this.lastQuery == this.allQuery){
16150 if(this.editable && !this.tickable){
16151 this.inputEl().dom.select();
16155 !this.selectByValue(this.value, true) &&
16158 !this.store.lastOptions ||
16159 typeof(this.store.lastOptions.add) == 'undefined' ||
16160 this.store.lastOptions.add != true
16163 this.select(0, true);
16166 if(this.autoFocus){
16169 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16170 this.taTask.delay(this.typeAheadDelay);
16174 this.onEmptyResults();
16180 onLoadException : function()
16182 this.hasQuery = false;
16184 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16185 this.loading.hide();
16188 if(this.tickable && this.editable){
16193 // only causes errors at present
16194 //Roo.log(this.store.reader.jsonData);
16195 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16197 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16203 onTypeAhead : function(){
16204 if(this.store.getCount() > 0){
16205 var r = this.store.getAt(0);
16206 var newValue = r.data[this.displayField];
16207 var len = newValue.length;
16208 var selStart = this.getRawValue().length;
16210 if(selStart != len){
16211 this.setRawValue(newValue);
16212 this.selectText(selStart, newValue.length);
16218 onSelect : function(record, index){
16220 if(this.fireEvent('beforeselect', this, record, index) !== false){
16222 this.setFromData(index > -1 ? record.data : false);
16225 this.fireEvent('select', this, record, index);
16230 * Returns the currently selected field value or empty string if no value is set.
16231 * @return {String} value The selected value
16233 getValue : function()
16235 if(Roo.isIOS && this.useNativeIOS){
16236 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16240 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16243 if(this.valueField){
16244 return typeof this.value != 'undefined' ? this.value : '';
16246 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16250 getRawValue : function()
16252 if(Roo.isIOS && this.useNativeIOS){
16253 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16256 var v = this.inputEl().getValue();
16262 * Clears any text/value currently set in the field
16264 clearValue : function(){
16266 if(this.hiddenField){
16267 this.hiddenField.dom.value = '';
16270 this.setRawValue('');
16271 this.lastSelectionText = '';
16272 this.lastData = false;
16274 var close = this.closeTriggerEl();
16285 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16286 * will be displayed in the field. If the value does not match the data value of an existing item,
16287 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16288 * Otherwise the field will be blank (although the value will still be set).
16289 * @param {String} value The value to match
16291 setValue : function(v)
16293 if(Roo.isIOS && this.useNativeIOS){
16294 this.setIOSValue(v);
16304 if(this.valueField){
16305 var r = this.findRecord(this.valueField, v);
16307 text = r.data[this.displayField];
16308 }else if(this.valueNotFoundText !== undefined){
16309 text = this.valueNotFoundText;
16312 this.lastSelectionText = text;
16313 if(this.hiddenField){
16314 this.hiddenField.dom.value = v;
16316 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16319 var close = this.closeTriggerEl();
16322 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16328 * @property {Object} the last set data for the element
16333 * Sets the value of the field based on a object which is related to the record format for the store.
16334 * @param {Object} value the value to set as. or false on reset?
16336 setFromData : function(o){
16343 var dv = ''; // display value
16344 var vv = ''; // value value..
16346 if (this.displayField) {
16347 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16349 // this is an error condition!!!
16350 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16353 if(this.valueField){
16354 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16357 var close = this.closeTriggerEl();
16360 if(dv.length || vv * 1 > 0){
16362 this.blockFocus=true;
16368 if(this.hiddenField){
16369 this.hiddenField.dom.value = vv;
16371 this.lastSelectionText = dv;
16372 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16376 // no hidden field.. - we store the value in 'value', but still display
16377 // display field!!!!
16378 this.lastSelectionText = dv;
16379 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16386 reset : function(){
16387 // overridden so that last data is reset..
16394 this.setValue(this.originalValue);
16395 //this.clearInvalid();
16396 this.lastData = false;
16398 this.view.clearSelections();
16404 findRecord : function(prop, value){
16406 if(this.store.getCount() > 0){
16407 this.store.each(function(r){
16408 if(r.data[prop] == value){
16418 getName: function()
16420 // returns hidden if it's set..
16421 if (!this.rendered) {return ''};
16422 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16426 onViewMove : function(e, t){
16427 this.inKeyMode = false;
16431 onViewOver : function(e, t){
16432 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16435 var item = this.view.findItemFromChild(t);
16438 var index = this.view.indexOf(item);
16439 this.select(index, false);
16444 onViewClick : function(view, doFocus, el, e)
16446 var index = this.view.getSelectedIndexes()[0];
16448 var r = this.store.getAt(index);
16452 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16459 Roo.each(this.tickItems, function(v,k){
16461 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16463 _this.tickItems.splice(k, 1);
16465 if(typeof(e) == 'undefined' && view == false){
16466 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16478 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16479 this.tickItems.push(r.data);
16482 if(typeof(e) == 'undefined' && view == false){
16483 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16490 this.onSelect(r, index);
16492 if(doFocus !== false && !this.blockFocus){
16493 this.inputEl().focus();
16498 restrictHeight : function(){
16499 //this.innerList.dom.style.height = '';
16500 //var inner = this.innerList.dom;
16501 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16502 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16503 //this.list.beginUpdate();
16504 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16505 this.list.alignTo(this.inputEl(), this.listAlign);
16506 this.list.alignTo(this.inputEl(), this.listAlign);
16507 //this.list.endUpdate();
16511 onEmptyResults : function(){
16513 if(this.tickable && this.editable){
16514 this.hasFocus = false;
16515 this.restrictHeight();
16523 * Returns true if the dropdown list is expanded, else false.
16525 isExpanded : function(){
16526 return this.list.isVisible();
16530 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16531 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16532 * @param {String} value The data value of the item to select
16533 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16534 * selected item if it is not currently in view (defaults to true)
16535 * @return {Boolean} True if the value matched an item in the list, else false
16537 selectByValue : function(v, scrollIntoView){
16538 if(v !== undefined && v !== null){
16539 var r = this.findRecord(this.valueField || this.displayField, v);
16541 this.select(this.store.indexOf(r), scrollIntoView);
16549 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16550 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16551 * @param {Number} index The zero-based index of the list item to select
16552 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16553 * selected item if it is not currently in view (defaults to true)
16555 select : function(index, scrollIntoView){
16556 this.selectedIndex = index;
16557 this.view.select(index);
16558 if(scrollIntoView !== false){
16559 var el = this.view.getNode(index);
16561 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16564 this.list.scrollChildIntoView(el, false);
16570 selectNext : function(){
16571 var ct = this.store.getCount();
16573 if(this.selectedIndex == -1){
16575 }else if(this.selectedIndex < ct-1){
16576 this.select(this.selectedIndex+1);
16582 selectPrev : function(){
16583 var ct = this.store.getCount();
16585 if(this.selectedIndex == -1){
16587 }else if(this.selectedIndex != 0){
16588 this.select(this.selectedIndex-1);
16594 onKeyUp : function(e){
16595 if(this.editable !== false && !e.isSpecialKey()){
16596 this.lastKey = e.getKey();
16597 this.dqTask.delay(this.queryDelay);
16602 validateBlur : function(){
16603 return !this.list || !this.list.isVisible();
16607 initQuery : function(){
16609 var v = this.getRawValue();
16611 if(this.tickable && this.editable){
16612 v = this.tickableInputEl().getValue();
16619 doForce : function(){
16620 if(this.inputEl().dom.value.length > 0){
16621 this.inputEl().dom.value =
16622 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16628 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16629 * query allowing the query action to be canceled if needed.
16630 * @param {String} query The SQL query to execute
16631 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16632 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16633 * saved in the current store (defaults to false)
16635 doQuery : function(q, forceAll){
16637 if(q === undefined || q === null){
16642 forceAll: forceAll,
16646 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16651 forceAll = qe.forceAll;
16652 if(forceAll === true || (q.length >= this.minChars)){
16654 this.hasQuery = true;
16656 if(this.lastQuery != q || this.alwaysQuery){
16657 this.lastQuery = q;
16658 if(this.mode == 'local'){
16659 this.selectedIndex = -1;
16661 this.store.clearFilter();
16664 if(this.specialFilter){
16665 this.fireEvent('specialfilter', this);
16670 this.store.filter(this.displayField, q);
16673 this.store.fireEvent("datachanged", this.store);
16680 this.store.baseParams[this.queryParam] = q;
16682 var options = {params : this.getParams(q)};
16685 options.add = true;
16686 options.params.start = this.page * this.pageSize;
16689 this.store.load(options);
16692 * this code will make the page width larger, at the beginning, the list not align correctly,
16693 * we should expand the list on onLoad
16694 * so command out it
16699 this.selectedIndex = -1;
16704 this.loadNext = false;
16708 getParams : function(q){
16710 //p[this.queryParam] = q;
16714 p.limit = this.pageSize;
16720 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16722 collapse : function(){
16723 if(!this.isExpanded()){
16729 this.hasFocus = false;
16733 this.cancelBtn.hide();
16734 this.trigger.show();
16737 this.tickableInputEl().dom.value = '';
16738 this.tickableInputEl().blur();
16743 Roo.get(document).un('mousedown', this.collapseIf, this);
16744 Roo.get(document).un('mousewheel', this.collapseIf, this);
16745 if (!this.editable) {
16746 Roo.get(document).un('keydown', this.listKeyPress, this);
16748 this.fireEvent('collapse', this);
16754 collapseIf : function(e){
16755 var in_combo = e.within(this.el);
16756 var in_list = e.within(this.list);
16757 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16759 if (in_combo || in_list || is_list) {
16760 //e.stopPropagation();
16765 this.onTickableFooterButtonClick(e, false, false);
16773 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16775 expand : function(){
16777 if(this.isExpanded() || !this.hasFocus){
16781 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16782 this.list.setWidth(lw);
16788 this.restrictHeight();
16792 this.tickItems = Roo.apply([], this.item);
16795 this.cancelBtn.show();
16796 this.trigger.hide();
16799 this.tickableInputEl().focus();
16804 Roo.get(document).on('mousedown', this.collapseIf, this);
16805 Roo.get(document).on('mousewheel', this.collapseIf, this);
16806 if (!this.editable) {
16807 Roo.get(document).on('keydown', this.listKeyPress, this);
16810 this.fireEvent('expand', this);
16814 // Implements the default empty TriggerField.onTriggerClick function
16815 onTriggerClick : function(e)
16817 Roo.log('trigger click');
16819 if(this.disabled || !this.triggerList){
16824 this.loadNext = false;
16826 if(this.isExpanded()){
16828 if (!this.blockFocus) {
16829 this.inputEl().focus();
16833 this.hasFocus = true;
16834 if(this.triggerAction == 'all') {
16835 this.doQuery(this.allQuery, true);
16837 this.doQuery(this.getRawValue());
16839 if (!this.blockFocus) {
16840 this.inputEl().focus();
16845 onTickableTriggerClick : function(e)
16852 this.loadNext = false;
16853 this.hasFocus = true;
16855 if(this.triggerAction == 'all') {
16856 this.doQuery(this.allQuery, true);
16858 this.doQuery(this.getRawValue());
16862 onSearchFieldClick : function(e)
16864 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16865 this.onTickableFooterButtonClick(e, false, false);
16869 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16874 this.loadNext = false;
16875 this.hasFocus = true;
16877 if(this.triggerAction == 'all') {
16878 this.doQuery(this.allQuery, true);
16880 this.doQuery(this.getRawValue());
16884 listKeyPress : function(e)
16886 //Roo.log('listkeypress');
16887 // scroll to first matching element based on key pres..
16888 if (e.isSpecialKey()) {
16891 var k = String.fromCharCode(e.getKey()).toUpperCase();
16894 var csel = this.view.getSelectedNodes();
16895 var cselitem = false;
16897 var ix = this.view.indexOf(csel[0]);
16898 cselitem = this.store.getAt(ix);
16899 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16905 this.store.each(function(v) {
16907 // start at existing selection.
16908 if (cselitem.id == v.id) {
16914 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16915 match = this.store.indexOf(v);
16921 if (match === false) {
16922 return true; // no more action?
16925 this.view.select(match);
16926 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16927 sn.scrollIntoView(sn.dom.parentNode, false);
16930 onViewScroll : function(e, t){
16932 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){
16936 this.hasQuery = true;
16938 this.loading = this.list.select('.loading', true).first();
16940 if(this.loading === null){
16941 this.list.createChild({
16943 cls: 'loading roo-select2-more-results roo-select2-active',
16944 html: 'Loading more results...'
16947 this.loading = this.list.select('.loading', true).first();
16949 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16951 this.loading.hide();
16954 this.loading.show();
16959 this.loadNext = true;
16961 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16966 addItem : function(o)
16968 var dv = ''; // display value
16970 if (this.displayField) {
16971 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16973 // this is an error condition!!!
16974 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16981 var choice = this.choices.createChild({
16983 cls: 'roo-select2-search-choice',
16992 cls: 'roo-select2-search-choice-close fa fa-times',
16997 }, this.searchField);
16999 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17001 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17009 this.inputEl().dom.value = '';
17014 onRemoveItem : function(e, _self, o)
17016 e.preventDefault();
17018 this.lastItem = Roo.apply([], this.item);
17020 var index = this.item.indexOf(o.data) * 1;
17023 Roo.log('not this item?!');
17027 this.item.splice(index, 1);
17032 this.fireEvent('remove', this, e);
17038 syncValue : function()
17040 if(!this.item.length){
17047 Roo.each(this.item, function(i){
17048 if(_this.valueField){
17049 value.push(i[_this.valueField]);
17056 this.value = value.join(',');
17058 if(this.hiddenField){
17059 this.hiddenField.dom.value = this.value;
17062 this.store.fireEvent("datachanged", this.store);
17067 clearItem : function()
17069 if(!this.multiple){
17075 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17083 if(this.tickable && !Roo.isTouch){
17084 this.view.refresh();
17088 inputEl: function ()
17090 if(Roo.isIOS && this.useNativeIOS){
17091 return this.el.select('select.roo-ios-select', true).first();
17094 if(Roo.isTouch && this.mobileTouchView){
17095 return this.el.select('input.form-control',true).first();
17099 return this.searchField;
17102 return this.el.select('input.form-control',true).first();
17105 onTickableFooterButtonClick : function(e, btn, el)
17107 e.preventDefault();
17109 this.lastItem = Roo.apply([], this.item);
17111 if(btn && btn.name == 'cancel'){
17112 this.tickItems = Roo.apply([], this.item);
17121 Roo.each(this.tickItems, function(o){
17129 validate : function()
17131 if(this.getVisibilityEl().hasClass('hidden')){
17135 var v = this.getRawValue();
17138 v = this.getValue();
17141 if(this.disabled || this.allowBlank || v.length){
17146 this.markInvalid();
17150 tickableInputEl : function()
17152 if(!this.tickable || !this.editable){
17153 return this.inputEl();
17156 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17160 getAutoCreateTouchView : function()
17165 cls: 'form-group' //input-group
17171 type : this.inputType,
17172 cls : 'form-control x-combo-noedit',
17173 autocomplete: 'new-password',
17174 placeholder : this.placeholder || '',
17179 input.name = this.name;
17183 input.cls += ' input-' + this.size;
17186 if (this.disabled) {
17187 input.disabled = true;
17191 cls : 'roo-combobox-wrap',
17198 inputblock.cls += ' input-group';
17200 inputblock.cn.unshift({
17202 cls : 'input-group-addon input-group-prepend input-group-text',
17207 if(this.removable && !this.multiple){
17208 inputblock.cls += ' roo-removable';
17210 inputblock.cn.push({
17213 cls : 'roo-combo-removable-btn close'
17217 if(this.hasFeedback && !this.allowBlank){
17219 inputblock.cls += ' has-feedback';
17221 inputblock.cn.push({
17223 cls: 'glyphicon form-control-feedback'
17230 inputblock.cls += (this.before) ? '' : ' input-group';
17232 inputblock.cn.push({
17234 cls : 'input-group-addon input-group-append input-group-text',
17240 var ibwrap = inputblock;
17245 cls: 'roo-select2-choices',
17249 cls: 'roo-select2-search-field',
17262 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17267 cls: 'form-hidden-field'
17273 if(!this.multiple && this.showToggleBtn){
17279 if (this.caret != false) {
17282 cls: 'fa fa-' + this.caret
17289 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17291 Roo.bootstrap.version == 3 ? caret : '',
17294 cls: 'combobox-clear',
17308 combobox.cls += ' roo-select2-container-multi';
17311 var align = this.labelAlign || this.parentLabelAlign();
17313 if (align ==='left' && this.fieldLabel.length) {
17318 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17319 tooltip : 'This field is required'
17323 cls : 'control-label col-form-label',
17324 html : this.fieldLabel
17328 cls : 'roo-combobox-wrap ',
17335 var labelCfg = cfg.cn[1];
17336 var contentCfg = cfg.cn[2];
17339 if(this.indicatorpos == 'right'){
17344 cls : 'control-label col-form-label',
17348 html : this.fieldLabel
17352 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17353 tooltip : 'This field is required'
17358 cls : "roo-combobox-wrap ",
17366 labelCfg = cfg.cn[0];
17367 contentCfg = cfg.cn[1];
17372 if(this.labelWidth > 12){
17373 labelCfg.style = "width: " + this.labelWidth + 'px';
17376 if(this.labelWidth < 13 && this.labelmd == 0){
17377 this.labelmd = this.labelWidth;
17380 if(this.labellg > 0){
17381 labelCfg.cls += ' col-lg-' + this.labellg;
17382 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17385 if(this.labelmd > 0){
17386 labelCfg.cls += ' col-md-' + this.labelmd;
17387 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17390 if(this.labelsm > 0){
17391 labelCfg.cls += ' col-sm-' + this.labelsm;
17392 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17395 if(this.labelxs > 0){
17396 labelCfg.cls += ' col-xs-' + this.labelxs;
17397 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17401 } else if ( this.fieldLabel.length) {
17405 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17406 tooltip : 'This field is required'
17410 cls : 'control-label',
17411 html : this.fieldLabel
17422 if(this.indicatorpos == 'right'){
17426 cls : 'control-label',
17427 html : this.fieldLabel,
17431 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17432 tooltip : 'This field is required'
17449 var settings = this;
17451 ['xs','sm','md','lg'].map(function(size){
17452 if (settings[size]) {
17453 cfg.cls += ' col-' + size + '-' + settings[size];
17460 initTouchView : function()
17462 this.renderTouchView();
17464 this.touchViewEl.on('scroll', function(){
17465 this.el.dom.scrollTop = 0;
17468 this.originalValue = this.getValue();
17470 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17472 this.inputEl().on("click", this.showTouchView, this);
17473 if (this.triggerEl) {
17474 this.triggerEl.on("click", this.showTouchView, this);
17478 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17479 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17481 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17483 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17484 this.store.on('load', this.onTouchViewLoad, this);
17485 this.store.on('loadexception', this.onTouchViewLoadException, this);
17487 if(this.hiddenName){
17489 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17491 this.hiddenField.dom.value =
17492 this.hiddenValue !== undefined ? this.hiddenValue :
17493 this.value !== undefined ? this.value : '';
17495 this.el.dom.removeAttribute('name');
17496 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17500 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17501 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17504 if(this.removable && !this.multiple){
17505 var close = this.closeTriggerEl();
17507 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17508 close.on('click', this.removeBtnClick, this, close);
17512 * fix the bug in Safari iOS8
17514 this.inputEl().on("focus", function(e){
17515 document.activeElement.blur();
17518 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17525 renderTouchView : function()
17527 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17528 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17530 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17531 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17533 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17534 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17535 this.touchViewBodyEl.setStyle('overflow', 'auto');
17537 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17538 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17540 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17541 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17545 showTouchView : function()
17551 this.touchViewHeaderEl.hide();
17553 if(this.modalTitle.length){
17554 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17555 this.touchViewHeaderEl.show();
17558 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17559 this.touchViewEl.show();
17561 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17563 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17564 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17566 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17568 if(this.modalTitle.length){
17569 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17572 this.touchViewBodyEl.setHeight(bodyHeight);
17576 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17578 this.touchViewEl.addClass(['in','show']);
17581 if(this._touchViewMask){
17582 Roo.get(document.body).addClass("x-body-masked");
17583 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17584 this._touchViewMask.setStyle('z-index', 10000);
17585 this._touchViewMask.addClass('show');
17588 this.doTouchViewQuery();
17592 hideTouchView : function()
17594 this.touchViewEl.removeClass(['in','show']);
17598 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17600 this.touchViewEl.setStyle('display', 'none');
17603 if(this._touchViewMask){
17604 this._touchViewMask.removeClass('show');
17605 Roo.get(document.body).removeClass("x-body-masked");
17609 setTouchViewValue : function()
17616 Roo.each(this.tickItems, function(o){
17621 this.hideTouchView();
17624 doTouchViewQuery : function()
17633 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17637 if(!this.alwaysQuery || this.mode == 'local'){
17638 this.onTouchViewLoad();
17645 onTouchViewBeforeLoad : function(combo,opts)
17651 onTouchViewLoad : function()
17653 if(this.store.getCount() < 1){
17654 this.onTouchViewEmptyResults();
17658 this.clearTouchView();
17660 var rawValue = this.getRawValue();
17662 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17664 this.tickItems = [];
17666 this.store.data.each(function(d, rowIndex){
17667 var row = this.touchViewListGroup.createChild(template);
17669 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17670 row.addClass(d.data.cls);
17673 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17676 html : d.data[this.displayField]
17679 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17680 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17683 row.removeClass('selected');
17684 if(!this.multiple && this.valueField &&
17685 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17688 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689 row.addClass('selected');
17692 if(this.multiple && this.valueField &&
17693 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17697 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17698 this.tickItems.push(d.data);
17701 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17705 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17707 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17709 if(this.modalTitle.length){
17710 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17713 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17715 if(this.mobile_restrict_height && listHeight < bodyHeight){
17716 this.touchViewBodyEl.setHeight(listHeight);
17721 if(firstChecked && listHeight > bodyHeight){
17722 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17727 onTouchViewLoadException : function()
17729 this.hideTouchView();
17732 onTouchViewEmptyResults : function()
17734 this.clearTouchView();
17736 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17738 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17742 clearTouchView : function()
17744 this.touchViewListGroup.dom.innerHTML = '';
17747 onTouchViewClick : function(e, el, o)
17749 e.preventDefault();
17752 var rowIndex = o.rowIndex;
17754 var r = this.store.getAt(rowIndex);
17756 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17758 if(!this.multiple){
17759 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17760 c.dom.removeAttribute('checked');
17763 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17765 this.setFromData(r.data);
17767 var close = this.closeTriggerEl();
17773 this.hideTouchView();
17775 this.fireEvent('select', this, r, rowIndex);
17780 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17781 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17782 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17786 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17787 this.addItem(r.data);
17788 this.tickItems.push(r.data);
17792 getAutoCreateNativeIOS : function()
17795 cls: 'form-group' //input-group,
17800 cls : 'roo-ios-select'
17804 combobox.name = this.name;
17807 if (this.disabled) {
17808 combobox.disabled = true;
17811 var settings = this;
17813 ['xs','sm','md','lg'].map(function(size){
17814 if (settings[size]) {
17815 cfg.cls += ' col-' + size + '-' + settings[size];
17825 initIOSView : function()
17827 this.store.on('load', this.onIOSViewLoad, this);
17832 onIOSViewLoad : function()
17834 if(this.store.getCount() < 1){
17838 this.clearIOSView();
17840 if(this.allowBlank) {
17842 var default_text = '-- SELECT --';
17844 if(this.placeholder.length){
17845 default_text = this.placeholder;
17848 if(this.emptyTitle.length){
17849 default_text += ' - ' + this.emptyTitle + ' -';
17852 var opt = this.inputEl().createChild({
17855 html : default_text
17859 o[this.valueField] = 0;
17860 o[this.displayField] = default_text;
17862 this.ios_options.push({
17869 this.store.data.each(function(d, rowIndex){
17873 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17874 html = d.data[this.displayField];
17879 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17880 value = d.data[this.valueField];
17889 if(this.value == d.data[this.valueField]){
17890 option['selected'] = true;
17893 var opt = this.inputEl().createChild(option);
17895 this.ios_options.push({
17902 this.inputEl().on('change', function(){
17903 this.fireEvent('select', this);
17908 clearIOSView: function()
17910 this.inputEl().dom.innerHTML = '';
17912 this.ios_options = [];
17915 setIOSValue: function(v)
17919 if(!this.ios_options){
17923 Roo.each(this.ios_options, function(opts){
17925 opts.el.dom.removeAttribute('selected');
17927 if(opts.data[this.valueField] != v){
17931 opts.el.dom.setAttribute('selected', true);
17937 * @cfg {Boolean} grow
17941 * @cfg {Number} growMin
17945 * @cfg {Number} growMax
17954 Roo.apply(Roo.bootstrap.ComboBox, {
17958 cls: 'modal-header',
17980 cls: 'list-group-item',
17984 cls: 'roo-combobox-list-group-item-value'
17988 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18002 listItemCheckbox : {
18004 cls: 'list-group-item',
18008 cls: 'roo-combobox-list-group-item-value'
18012 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18028 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18033 cls: 'modal-footer',
18041 cls: 'col-xs-6 text-left',
18044 cls: 'btn btn-danger roo-touch-view-cancel',
18050 cls: 'col-xs-6 text-right',
18053 cls: 'btn btn-success roo-touch-view-ok',
18064 Roo.apply(Roo.bootstrap.ComboBox, {
18066 touchViewTemplate : {
18068 cls: 'modal fade roo-combobox-touch-view',
18072 cls: 'modal-dialog',
18073 style : 'position:fixed', // we have to fix position....
18077 cls: 'modal-content',
18079 Roo.bootstrap.ComboBox.header,
18080 Roo.bootstrap.ComboBox.body,
18081 Roo.bootstrap.ComboBox.footer
18090 * Ext JS Library 1.1.1
18091 * Copyright(c) 2006-2007, Ext JS, LLC.
18093 * Originally Released Under LGPL - original licence link has changed is not relivant.
18096 * <script type="text/javascript">
18101 * @extends Roo.util.Observable
18102 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18103 * This class also supports single and multi selection modes. <br>
18104 * Create a data model bound view:
18106 var store = new Roo.data.Store(...);
18108 var view = new Roo.View({
18110 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18112 singleSelect: true,
18113 selectedClass: "ydataview-selected",
18117 // listen for node click?
18118 view.on("click", function(vw, index, node, e){
18119 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18123 dataModel.load("foobar.xml");
18125 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18127 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18128 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18130 * Note: old style constructor is still suported (container, template, config)
18133 * Create a new View
18134 * @param {Object} config The config object
18137 Roo.View = function(config, depreciated_tpl, depreciated_config){
18139 this.parent = false;
18141 if (typeof(depreciated_tpl) == 'undefined') {
18142 // new way.. - universal constructor.
18143 Roo.apply(this, config);
18144 this.el = Roo.get(this.el);
18147 this.el = Roo.get(config);
18148 this.tpl = depreciated_tpl;
18149 Roo.apply(this, depreciated_config);
18151 this.wrapEl = this.el.wrap().wrap();
18152 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18155 if(typeof(this.tpl) == "string"){
18156 this.tpl = new Roo.Template(this.tpl);
18158 // support xtype ctors..
18159 this.tpl = new Roo.factory(this.tpl, Roo);
18163 this.tpl.compile();
18168 * @event beforeclick
18169 * Fires before a click is processed. Returns false to cancel the default action.
18170 * @param {Roo.View} this
18171 * @param {Number} index The index of the target node
18172 * @param {HTMLElement} node The target node
18173 * @param {Roo.EventObject} e The raw event object
18175 "beforeclick" : true,
18178 * Fires when a template node is clicked.
18179 * @param {Roo.View} this
18180 * @param {Number} index The index of the target node
18181 * @param {HTMLElement} node The target node
18182 * @param {Roo.EventObject} e The raw event object
18187 * Fires when a template node is double clicked.
18188 * @param {Roo.View} this
18189 * @param {Number} index The index of the target node
18190 * @param {HTMLElement} node The target node
18191 * @param {Roo.EventObject} e The raw event object
18195 * @event contextmenu
18196 * Fires when a template node is right clicked.
18197 * @param {Roo.View} this
18198 * @param {Number} index The index of the target node
18199 * @param {HTMLElement} node The target node
18200 * @param {Roo.EventObject} e The raw event object
18202 "contextmenu" : true,
18204 * @event selectionchange
18205 * Fires when the selected nodes change.
18206 * @param {Roo.View} this
18207 * @param {Array} selections Array of the selected nodes
18209 "selectionchange" : true,
18212 * @event beforeselect
18213 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18214 * @param {Roo.View} this
18215 * @param {HTMLElement} node The node to be selected
18216 * @param {Array} selections Array of currently selected nodes
18218 "beforeselect" : true,
18220 * @event preparedata
18221 * Fires on every row to render, to allow you to change the data.
18222 * @param {Roo.View} this
18223 * @param {Object} data to be rendered (change this)
18225 "preparedata" : true
18233 "click": this.onClick,
18234 "dblclick": this.onDblClick,
18235 "contextmenu": this.onContextMenu,
18239 this.selections = [];
18241 this.cmp = new Roo.CompositeElementLite([]);
18243 this.store = Roo.factory(this.store, Roo.data);
18244 this.setStore(this.store, true);
18247 if ( this.footer && this.footer.xtype) {
18249 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18251 this.footer.dataSource = this.store;
18252 this.footer.container = fctr;
18253 this.footer = Roo.factory(this.footer, Roo);
18254 fctr.insertFirst(this.el);
18256 // this is a bit insane - as the paging toolbar seems to detach the el..
18257 // dom.parentNode.parentNode.parentNode
18258 // they get detached?
18262 Roo.View.superclass.constructor.call(this);
18267 Roo.extend(Roo.View, Roo.util.Observable, {
18270 * @cfg {Roo.data.Store} store Data store to load data from.
18275 * @cfg {String|Roo.Element} el The container element.
18280 * @cfg {String|Roo.Template} tpl The template used by this View
18284 * @cfg {String} dataName the named area of the template to use as the data area
18285 * Works with domtemplates roo-name="name"
18289 * @cfg {String} selectedClass The css class to add to selected nodes
18291 selectedClass : "x-view-selected",
18293 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18298 * @cfg {String} text to display on mask (default Loading)
18302 * @cfg {Boolean} multiSelect Allow multiple selection
18304 multiSelect : false,
18306 * @cfg {Boolean} singleSelect Allow single selection
18308 singleSelect: false,
18311 * @cfg {Boolean} toggleSelect - selecting
18313 toggleSelect : false,
18316 * @cfg {Boolean} tickable - selecting
18321 * Returns the element this view is bound to.
18322 * @return {Roo.Element}
18324 getEl : function(){
18325 return this.wrapEl;
18331 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18333 refresh : function(){
18334 //Roo.log('refresh');
18337 // if we are using something like 'domtemplate', then
18338 // the what gets used is:
18339 // t.applySubtemplate(NAME, data, wrapping data..)
18340 // the outer template then get' applied with
18341 // the store 'extra data'
18342 // and the body get's added to the
18343 // roo-name="data" node?
18344 // <span class='roo-tpl-{name}'></span> ?????
18348 this.clearSelections();
18349 this.el.update("");
18351 var records = this.store.getRange();
18352 if(records.length < 1) {
18354 // is this valid?? = should it render a template??
18356 this.el.update(this.emptyText);
18360 if (this.dataName) {
18361 this.el.update(t.apply(this.store.meta)); //????
18362 el = this.el.child('.roo-tpl-' + this.dataName);
18365 for(var i = 0, len = records.length; i < len; i++){
18366 var data = this.prepareData(records[i].data, i, records[i]);
18367 this.fireEvent("preparedata", this, data, i, records[i]);
18369 var d = Roo.apply({}, data);
18372 Roo.apply(d, {'roo-id' : Roo.id()});
18376 Roo.each(this.parent.item, function(item){
18377 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18380 Roo.apply(d, {'roo-data-checked' : 'checked'});
18384 html[html.length] = Roo.util.Format.trim(
18386 t.applySubtemplate(this.dataName, d, this.store.meta) :
18393 el.update(html.join(""));
18394 this.nodes = el.dom.childNodes;
18395 this.updateIndexes(0);
18400 * Function to override to reformat the data that is sent to
18401 * the template for each node.
18402 * DEPRICATED - use the preparedata event handler.
18403 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18404 * a JSON object for an UpdateManager bound view).
18406 prepareData : function(data, index, record)
18408 this.fireEvent("preparedata", this, data, index, record);
18412 onUpdate : function(ds, record){
18413 // Roo.log('on update');
18414 this.clearSelections();
18415 var index = this.store.indexOf(record);
18416 var n = this.nodes[index];
18417 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18418 n.parentNode.removeChild(n);
18419 this.updateIndexes(index, index);
18425 onAdd : function(ds, records, index)
18427 //Roo.log(['on Add', ds, records, index] );
18428 this.clearSelections();
18429 if(this.nodes.length == 0){
18433 var n = this.nodes[index];
18434 for(var i = 0, len = records.length; i < len; i++){
18435 var d = this.prepareData(records[i].data, i, records[i]);
18437 this.tpl.insertBefore(n, d);
18440 this.tpl.append(this.el, d);
18443 this.updateIndexes(index);
18446 onRemove : function(ds, record, index){
18447 // Roo.log('onRemove');
18448 this.clearSelections();
18449 var el = this.dataName ?
18450 this.el.child('.roo-tpl-' + this.dataName) :
18453 el.dom.removeChild(this.nodes[index]);
18454 this.updateIndexes(index);
18458 * Refresh an individual node.
18459 * @param {Number} index
18461 refreshNode : function(index){
18462 this.onUpdate(this.store, this.store.getAt(index));
18465 updateIndexes : function(startIndex, endIndex){
18466 var ns = this.nodes;
18467 startIndex = startIndex || 0;
18468 endIndex = endIndex || ns.length - 1;
18469 for(var i = startIndex; i <= endIndex; i++){
18470 ns[i].nodeIndex = i;
18475 * Changes the data store this view uses and refresh the view.
18476 * @param {Store} store
18478 setStore : function(store, initial){
18479 if(!initial && this.store){
18480 this.store.un("datachanged", this.refresh);
18481 this.store.un("add", this.onAdd);
18482 this.store.un("remove", this.onRemove);
18483 this.store.un("update", this.onUpdate);
18484 this.store.un("clear", this.refresh);
18485 this.store.un("beforeload", this.onBeforeLoad);
18486 this.store.un("load", this.onLoad);
18487 this.store.un("loadexception", this.onLoad);
18491 store.on("datachanged", this.refresh, this);
18492 store.on("add", this.onAdd, this);
18493 store.on("remove", this.onRemove, this);
18494 store.on("update", this.onUpdate, this);
18495 store.on("clear", this.refresh, this);
18496 store.on("beforeload", this.onBeforeLoad, this);
18497 store.on("load", this.onLoad, this);
18498 store.on("loadexception", this.onLoad, this);
18506 * onbeforeLoad - masks the loading area.
18509 onBeforeLoad : function(store,opts)
18511 //Roo.log('onBeforeLoad');
18513 this.el.update("");
18515 this.el.mask(this.mask ? this.mask : "Loading" );
18517 onLoad : function ()
18524 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18525 * @param {HTMLElement} node
18526 * @return {HTMLElement} The template node
18528 findItemFromChild : function(node){
18529 var el = this.dataName ?
18530 this.el.child('.roo-tpl-' + this.dataName,true) :
18533 if(!node || node.parentNode == el){
18536 var p = node.parentNode;
18537 while(p && p != el){
18538 if(p.parentNode == el){
18547 onClick : function(e){
18548 var item = this.findItemFromChild(e.getTarget());
18550 var index = this.indexOf(item);
18551 if(this.onItemClick(item, index, e) !== false){
18552 this.fireEvent("click", this, index, item, e);
18555 this.clearSelections();
18560 onContextMenu : function(e){
18561 var item = this.findItemFromChild(e.getTarget());
18563 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18568 onDblClick : function(e){
18569 var item = this.findItemFromChild(e.getTarget());
18571 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18575 onItemClick : function(item, index, e)
18577 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18580 if (this.toggleSelect) {
18581 var m = this.isSelected(item) ? 'unselect' : 'select';
18584 _t[m](item, true, false);
18587 if(this.multiSelect || this.singleSelect){
18588 if(this.multiSelect && e.shiftKey && this.lastSelection){
18589 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18591 this.select(item, this.multiSelect && e.ctrlKey);
18592 this.lastSelection = item;
18595 if(!this.tickable){
18596 e.preventDefault();
18604 * Get the number of selected nodes.
18607 getSelectionCount : function(){
18608 return this.selections.length;
18612 * Get the currently selected nodes.
18613 * @return {Array} An array of HTMLElements
18615 getSelectedNodes : function(){
18616 return this.selections;
18620 * Get the indexes of the selected nodes.
18623 getSelectedIndexes : function(){
18624 var indexes = [], s = this.selections;
18625 for(var i = 0, len = s.length; i < len; i++){
18626 indexes.push(s[i].nodeIndex);
18632 * Clear all selections
18633 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18635 clearSelections : function(suppressEvent){
18636 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18637 this.cmp.elements = this.selections;
18638 this.cmp.removeClass(this.selectedClass);
18639 this.selections = [];
18640 if(!suppressEvent){
18641 this.fireEvent("selectionchange", this, this.selections);
18647 * Returns true if the passed node is selected
18648 * @param {HTMLElement/Number} node The node or node index
18649 * @return {Boolean}
18651 isSelected : function(node){
18652 var s = this.selections;
18656 node = this.getNode(node);
18657 return s.indexOf(node) !== -1;
18662 * @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
18663 * @param {Boolean} keepExisting (optional) true to keep existing selections
18664 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18666 select : function(nodeInfo, keepExisting, suppressEvent){
18667 if(nodeInfo instanceof Array){
18669 this.clearSelections(true);
18671 for(var i = 0, len = nodeInfo.length; i < len; i++){
18672 this.select(nodeInfo[i], true, true);
18676 var node = this.getNode(nodeInfo);
18677 if(!node || this.isSelected(node)){
18678 return; // already selected.
18681 this.clearSelections(true);
18684 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18685 Roo.fly(node).addClass(this.selectedClass);
18686 this.selections.push(node);
18687 if(!suppressEvent){
18688 this.fireEvent("selectionchange", this, this.selections);
18696 * @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
18697 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18698 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18700 unselect : function(nodeInfo, keepExisting, suppressEvent)
18702 if(nodeInfo instanceof Array){
18703 Roo.each(this.selections, function(s) {
18704 this.unselect(s, nodeInfo);
18708 var node = this.getNode(nodeInfo);
18709 if(!node || !this.isSelected(node)){
18710 //Roo.log("not selected");
18711 return; // not selected.
18715 Roo.each(this.selections, function(s) {
18717 Roo.fly(node).removeClass(this.selectedClass);
18724 this.selections= ns;
18725 this.fireEvent("selectionchange", this, this.selections);
18729 * Gets a template node.
18730 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18731 * @return {HTMLElement} The node or null if it wasn't found
18733 getNode : function(nodeInfo){
18734 if(typeof nodeInfo == "string"){
18735 return document.getElementById(nodeInfo);
18736 }else if(typeof nodeInfo == "number"){
18737 return this.nodes[nodeInfo];
18743 * Gets a range template nodes.
18744 * @param {Number} startIndex
18745 * @param {Number} endIndex
18746 * @return {Array} An array of nodes
18748 getNodes : function(start, end){
18749 var ns = this.nodes;
18750 start = start || 0;
18751 end = typeof end == "undefined" ? ns.length - 1 : end;
18754 for(var i = start; i <= end; i++){
18758 for(var i = start; i >= end; i--){
18766 * Finds the index of the passed node
18767 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18768 * @return {Number} The index of the node or -1
18770 indexOf : function(node){
18771 node = this.getNode(node);
18772 if(typeof node.nodeIndex == "number"){
18773 return node.nodeIndex;
18775 var ns = this.nodes;
18776 for(var i = 0, len = ns.length; i < len; i++){
18787 * based on jquery fullcalendar
18791 Roo.bootstrap = Roo.bootstrap || {};
18793 * @class Roo.bootstrap.Calendar
18794 * @extends Roo.bootstrap.Component
18795 * Bootstrap Calendar class
18796 * @cfg {Boolean} loadMask (true|false) default false
18797 * @cfg {Object} header generate the user specific header of the calendar, default false
18800 * Create a new Container
18801 * @param {Object} config The config object
18806 Roo.bootstrap.Calendar = function(config){
18807 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18811 * Fires when a date is selected
18812 * @param {DatePicker} this
18813 * @param {Date} date The selected date
18817 * @event monthchange
18818 * Fires when the displayed month changes
18819 * @param {DatePicker} this
18820 * @param {Date} date The selected month
18822 'monthchange': true,
18824 * @event evententer
18825 * Fires when mouse over an event
18826 * @param {Calendar} this
18827 * @param {event} Event
18829 'evententer': true,
18831 * @event eventleave
18832 * Fires when the mouse leaves an
18833 * @param {Calendar} this
18836 'eventleave': true,
18838 * @event eventclick
18839 * Fires when the mouse click an
18840 * @param {Calendar} this
18849 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18852 * @cfg {Number} startDay
18853 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18861 getAutoCreate : function(){
18864 var fc_button = function(name, corner, style, content ) {
18865 return Roo.apply({},{
18867 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18869 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18872 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18883 style : 'width:100%',
18890 cls : 'fc-header-left',
18892 fc_button('prev', 'left', 'arrow', '‹' ),
18893 fc_button('next', 'right', 'arrow', '›' ),
18894 { tag: 'span', cls: 'fc-header-space' },
18895 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18903 cls : 'fc-header-center',
18907 cls: 'fc-header-title',
18910 html : 'month / year'
18918 cls : 'fc-header-right',
18920 /* fc_button('month', 'left', '', 'month' ),
18921 fc_button('week', '', '', 'week' ),
18922 fc_button('day', 'right', '', 'day' )
18934 header = this.header;
18937 var cal_heads = function() {
18939 // fixme - handle this.
18941 for (var i =0; i < Date.dayNames.length; i++) {
18942 var d = Date.dayNames[i];
18945 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18946 html : d.substring(0,3)
18950 ret[0].cls += ' fc-first';
18951 ret[6].cls += ' fc-last';
18954 var cal_cell = function(n) {
18957 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18962 cls: 'fc-day-number',
18966 cls: 'fc-day-content',
18970 style: 'position: relative;' // height: 17px;
18982 var cal_rows = function() {
18985 for (var r = 0; r < 6; r++) {
18992 for (var i =0; i < Date.dayNames.length; i++) {
18993 var d = Date.dayNames[i];
18994 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18997 row.cn[0].cls+=' fc-first';
18998 row.cn[0].cn[0].style = 'min-height:90px';
18999 row.cn[6].cls+=' fc-last';
19003 ret[0].cls += ' fc-first';
19004 ret[4].cls += ' fc-prev-last';
19005 ret[5].cls += ' fc-last';
19012 cls: 'fc-border-separate',
19013 style : 'width:100%',
19021 cls : 'fc-first fc-last',
19039 cls : 'fc-content',
19040 style : "position: relative;",
19043 cls : 'fc-view fc-view-month fc-grid',
19044 style : 'position: relative',
19045 unselectable : 'on',
19048 cls : 'fc-event-container',
19049 style : 'position:absolute;z-index:8;top:0;left:0;'
19067 initEvents : function()
19070 throw "can not find store for calendar";
19076 style: "text-align:center",
19080 style: "background-color:white;width:50%;margin:250 auto",
19084 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19095 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19097 var size = this.el.select('.fc-content', true).first().getSize();
19098 this.maskEl.setSize(size.width, size.height);
19099 this.maskEl.enableDisplayMode("block");
19100 if(!this.loadMask){
19101 this.maskEl.hide();
19104 this.store = Roo.factory(this.store, Roo.data);
19105 this.store.on('load', this.onLoad, this);
19106 this.store.on('beforeload', this.onBeforeLoad, this);
19110 this.cells = this.el.select('.fc-day',true);
19111 //Roo.log(this.cells);
19112 this.textNodes = this.el.query('.fc-day-number');
19113 this.cells.addClassOnOver('fc-state-hover');
19115 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19116 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19117 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19118 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19120 this.on('monthchange', this.onMonthChange, this);
19122 this.update(new Date().clearTime());
19125 resize : function() {
19126 var sz = this.el.getSize();
19128 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19129 this.el.select('.fc-day-content div',true).setHeight(34);
19134 showPrevMonth : function(e){
19135 this.update(this.activeDate.add("mo", -1));
19137 showToday : function(e){
19138 this.update(new Date().clearTime());
19141 showNextMonth : function(e){
19142 this.update(this.activeDate.add("mo", 1));
19146 showPrevYear : function(){
19147 this.update(this.activeDate.add("y", -1));
19151 showNextYear : function(){
19152 this.update(this.activeDate.add("y", 1));
19157 update : function(date)
19159 var vd = this.activeDate;
19160 this.activeDate = date;
19161 // if(vd && this.el){
19162 // var t = date.getTime();
19163 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19164 // Roo.log('using add remove');
19166 // this.fireEvent('monthchange', this, date);
19168 // this.cells.removeClass("fc-state-highlight");
19169 // this.cells.each(function(c){
19170 // if(c.dateValue == t){
19171 // c.addClass("fc-state-highlight");
19172 // setTimeout(function(){
19173 // try{c.dom.firstChild.focus();}catch(e){}
19183 var days = date.getDaysInMonth();
19185 var firstOfMonth = date.getFirstDateOfMonth();
19186 var startingPos = firstOfMonth.getDay()-this.startDay;
19188 if(startingPos < this.startDay){
19192 var pm = date.add(Date.MONTH, -1);
19193 var prevStart = pm.getDaysInMonth()-startingPos;
19195 this.cells = this.el.select('.fc-day',true);
19196 this.textNodes = this.el.query('.fc-day-number');
19197 this.cells.addClassOnOver('fc-state-hover');
19199 var cells = this.cells.elements;
19200 var textEls = this.textNodes;
19202 Roo.each(cells, function(cell){
19203 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19206 days += startingPos;
19208 // convert everything to numbers so it's fast
19209 var day = 86400000;
19210 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19213 //Roo.log(prevStart);
19215 var today = new Date().clearTime().getTime();
19216 var sel = date.clearTime().getTime();
19217 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19218 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19219 var ddMatch = this.disabledDatesRE;
19220 var ddText = this.disabledDatesText;
19221 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19222 var ddaysText = this.disabledDaysText;
19223 var format = this.format;
19225 var setCellClass = function(cal, cell){
19229 //Roo.log('set Cell Class');
19231 var t = d.getTime();
19235 cell.dateValue = t;
19237 cell.className += " fc-today";
19238 cell.className += " fc-state-highlight";
19239 cell.title = cal.todayText;
19242 // disable highlight in other month..
19243 //cell.className += " fc-state-highlight";
19248 cell.className = " fc-state-disabled";
19249 cell.title = cal.minText;
19253 cell.className = " fc-state-disabled";
19254 cell.title = cal.maxText;
19258 if(ddays.indexOf(d.getDay()) != -1){
19259 cell.title = ddaysText;
19260 cell.className = " fc-state-disabled";
19263 if(ddMatch && format){
19264 var fvalue = d.dateFormat(format);
19265 if(ddMatch.test(fvalue)){
19266 cell.title = ddText.replace("%0", fvalue);
19267 cell.className = " fc-state-disabled";
19271 if (!cell.initialClassName) {
19272 cell.initialClassName = cell.dom.className;
19275 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19280 for(; i < startingPos; i++) {
19281 textEls[i].innerHTML = (++prevStart);
19282 d.setDate(d.getDate()+1);
19284 cells[i].className = "fc-past fc-other-month";
19285 setCellClass(this, cells[i]);
19290 for(; i < days; i++){
19291 intDay = i - startingPos + 1;
19292 textEls[i].innerHTML = (intDay);
19293 d.setDate(d.getDate()+1);
19295 cells[i].className = ''; // "x-date-active";
19296 setCellClass(this, cells[i]);
19300 for(; i < 42; i++) {
19301 textEls[i].innerHTML = (++extraDays);
19302 d.setDate(d.getDate()+1);
19304 cells[i].className = "fc-future fc-other-month";
19305 setCellClass(this, cells[i]);
19308 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19310 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19312 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19313 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19315 if(totalRows != 6){
19316 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19317 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19320 this.fireEvent('monthchange', this, date);
19324 if(!this.internalRender){
19325 var main = this.el.dom.firstChild;
19326 var w = main.offsetWidth;
19327 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19328 Roo.fly(main).setWidth(w);
19329 this.internalRender = true;
19330 // opera does not respect the auto grow header center column
19331 // then, after it gets a width opera refuses to recalculate
19332 // without a second pass
19333 if(Roo.isOpera && !this.secondPass){
19334 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19335 this.secondPass = true;
19336 this.update.defer(10, this, [date]);
19343 findCell : function(dt) {
19344 dt = dt.clearTime().getTime();
19346 this.cells.each(function(c){
19347 //Roo.log("check " +c.dateValue + '?=' + dt);
19348 if(c.dateValue == dt){
19358 findCells : function(ev) {
19359 var s = ev.start.clone().clearTime().getTime();
19361 var e= ev.end.clone().clearTime().getTime();
19364 this.cells.each(function(c){
19365 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19367 if(c.dateValue > e){
19370 if(c.dateValue < s){
19379 // findBestRow: function(cells)
19383 // for (var i =0 ; i < cells.length;i++) {
19384 // ret = Math.max(cells[i].rows || 0,ret);
19391 addItem : function(ev)
19393 // look for vertical location slot in
19394 var cells = this.findCells(ev);
19396 // ev.row = this.findBestRow(cells);
19398 // work out the location.
19402 for(var i =0; i < cells.length; i++) {
19404 cells[i].row = cells[0].row;
19407 cells[i].row = cells[i].row + 1;
19417 if (crow.start.getY() == cells[i].getY()) {
19419 crow.end = cells[i];
19436 cells[0].events.push(ev);
19438 this.calevents.push(ev);
19441 clearEvents: function() {
19443 if(!this.calevents){
19447 Roo.each(this.cells.elements, function(c){
19453 Roo.each(this.calevents, function(e) {
19454 Roo.each(e.els, function(el) {
19455 el.un('mouseenter' ,this.onEventEnter, this);
19456 el.un('mouseleave' ,this.onEventLeave, this);
19461 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19467 renderEvents: function()
19471 this.cells.each(function(c) {
19480 if(c.row != c.events.length){
19481 r = 4 - (4 - (c.row - c.events.length));
19484 c.events = ev.slice(0, r);
19485 c.more = ev.slice(r);
19487 if(c.more.length && c.more.length == 1){
19488 c.events.push(c.more.pop());
19491 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19495 this.cells.each(function(c) {
19497 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19500 for (var e = 0; e < c.events.length; e++){
19501 var ev = c.events[e];
19502 var rows = ev.rows;
19504 for(var i = 0; i < rows.length; i++) {
19506 // how many rows should it span..
19509 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19510 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19512 unselectable : "on",
19515 cls: 'fc-event-inner',
19519 // cls: 'fc-event-time',
19520 // html : cells.length > 1 ? '' : ev.time
19524 cls: 'fc-event-title',
19525 html : String.format('{0}', ev.title)
19532 cls: 'ui-resizable-handle ui-resizable-e',
19533 html : '  '
19540 cfg.cls += ' fc-event-start';
19542 if ((i+1) == rows.length) {
19543 cfg.cls += ' fc-event-end';
19546 var ctr = _this.el.select('.fc-event-container',true).first();
19547 var cg = ctr.createChild(cfg);
19549 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19550 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19552 var r = (c.more.length) ? 1 : 0;
19553 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19554 cg.setWidth(ebox.right - sbox.x -2);
19556 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19557 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19558 cg.on('click', _this.onEventClick, _this, ev);
19569 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19570 style : 'position: absolute',
19571 unselectable : "on",
19574 cls: 'fc-event-inner',
19578 cls: 'fc-event-title',
19586 cls: 'ui-resizable-handle ui-resizable-e',
19587 html : '  '
19593 var ctr = _this.el.select('.fc-event-container',true).first();
19594 var cg = ctr.createChild(cfg);
19596 var sbox = c.select('.fc-day-content',true).first().getBox();
19597 var ebox = c.select('.fc-day-content',true).first().getBox();
19599 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19600 cg.setWidth(ebox.right - sbox.x -2);
19602 cg.on('click', _this.onMoreEventClick, _this, c.more);
19612 onEventEnter: function (e, el,event,d) {
19613 this.fireEvent('evententer', this, el, event);
19616 onEventLeave: function (e, el,event,d) {
19617 this.fireEvent('eventleave', this, el, event);
19620 onEventClick: function (e, el,event,d) {
19621 this.fireEvent('eventclick', this, el, event);
19624 onMonthChange: function () {
19628 onMoreEventClick: function(e, el, more)
19632 this.calpopover.placement = 'right';
19633 this.calpopover.setTitle('More');
19635 this.calpopover.setContent('');
19637 var ctr = this.calpopover.el.select('.popover-content', true).first();
19639 Roo.each(more, function(m){
19641 cls : 'fc-event-hori fc-event-draggable',
19644 var cg = ctr.createChild(cfg);
19646 cg.on('click', _this.onEventClick, _this, m);
19649 this.calpopover.show(el);
19654 onLoad: function ()
19656 this.calevents = [];
19659 if(this.store.getCount() > 0){
19660 this.store.data.each(function(d){
19663 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19664 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19665 time : d.data.start_time,
19666 title : d.data.title,
19667 description : d.data.description,
19668 venue : d.data.venue
19673 this.renderEvents();
19675 if(this.calevents.length && this.loadMask){
19676 this.maskEl.hide();
19680 onBeforeLoad: function()
19682 this.clearEvents();
19684 this.maskEl.show();
19698 * @class Roo.bootstrap.Popover
19699 * @extends Roo.bootstrap.Component
19700 * Bootstrap Popover class
19701 * @cfg {String} html contents of the popover (or false to use children..)
19702 * @cfg {String} title of popover (or false to hide)
19703 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19704 * @cfg {String} trigger click || hover (or false to trigger manually)
19705 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19706 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19707 * - if false and it has a 'parent' then it will be automatically added to that element
19708 * - if string - Roo.get will be called
19709 * @cfg {Number} delay - delay before showing
19712 * Create a new Popover
19713 * @param {Object} config The config object
19716 Roo.bootstrap.Popover = function(config){
19717 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19723 * After the popover show
19725 * @param {Roo.bootstrap.Popover} this
19730 * After the popover hide
19732 * @param {Roo.bootstrap.Popover} this
19738 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19743 placement : 'right',
19744 trigger : 'hover', // hover
19750 can_build_overlaid : false,
19752 maskEl : false, // the mask element
19755 alignEl : false, // when show is called with an element - this get's stored.
19757 getChildContainer : function()
19759 return this.contentEl;
19762 getPopoverHeader : function()
19764 this.title = true; // flag not to hide it..
19765 this.headerEl.addClass('p-0');
19766 return this.headerEl
19770 getAutoCreate : function(){
19773 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19774 style: 'display:block',
19780 cls : 'popover-inner ',
19784 cls: 'popover-title popover-header',
19785 html : this.title === false ? '' : this.title
19788 cls : 'popover-content popover-body ' + (this.cls || ''),
19789 html : this.html || ''
19800 * @param {string} the title
19802 setTitle: function(str)
19806 this.headerEl.dom.innerHTML = str;
19811 * @param {string} the body content
19813 setContent: function(str)
19816 if (this.contentEl) {
19817 this.contentEl.dom.innerHTML = str;
19821 // as it get's added to the bottom of the page.
19822 onRender : function(ct, position)
19824 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19829 var cfg = Roo.apply({}, this.getAutoCreate());
19833 cfg.cls += ' ' + this.cls;
19836 cfg.style = this.style;
19838 //Roo.log("adding to ");
19839 this.el = Roo.get(document.body).createChild(cfg, position);
19840 // Roo.log(this.el);
19843 this.contentEl = this.el.select('.popover-content',true).first();
19844 this.headerEl = this.el.select('.popover-title',true).first();
19847 if(typeof(this.items) != 'undefined'){
19848 var items = this.items;
19851 for(var i =0;i < items.length;i++) {
19852 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19856 this.items = nitems;
19858 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19859 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19866 resizeMask : function()
19868 this.maskEl.setSize(
19869 Roo.lib.Dom.getViewWidth(true),
19870 Roo.lib.Dom.getViewHeight(true)
19874 initEvents : function()
19878 Roo.bootstrap.Popover.register(this);
19881 this.arrowEl = this.el.select('.arrow',true).first();
19882 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19883 this.el.enableDisplayMode('block');
19887 if (this.over === false && !this.parent()) {
19890 if (this.triggers === false) {
19895 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19896 var triggers = this.trigger ? this.trigger.split(' ') : [];
19897 Roo.each(triggers, function(trigger) {
19899 if (trigger == 'click') {
19900 on_el.on('click', this.toggle, this);
19901 } else if (trigger != 'manual') {
19902 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19903 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19905 on_el.on(eventIn ,this.enter, this);
19906 on_el.on(eventOut, this.leave, this);
19916 toggle : function () {
19917 this.hoverState == 'in' ? this.leave() : this.enter();
19920 enter : function () {
19922 clearTimeout(this.timeout);
19924 this.hoverState = 'in';
19926 if (!this.delay || !this.delay.show) {
19931 this.timeout = setTimeout(function () {
19932 if (_t.hoverState == 'in') {
19935 }, this.delay.show)
19938 leave : function() {
19939 clearTimeout(this.timeout);
19941 this.hoverState = 'out';
19943 if (!this.delay || !this.delay.hide) {
19948 this.timeout = setTimeout(function () {
19949 if (_t.hoverState == 'out') {
19952 }, this.delay.hide)
19956 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19957 * @param {string} (left|right|top|bottom) position
19959 show : function (on_el, placement)
19961 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19962 on_el = on_el || false; // default to false
19965 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19966 on_el = this.parent().el;
19967 } else if (this.over) {
19968 Roo.get(this.over);
19973 this.alignEl = Roo.get( on_el );
19976 this.render(document.body);
19982 if (this.title === false) {
19983 this.headerEl.hide();
19988 this.el.dom.style.display = 'block';
19991 if (this.alignEl) {
19992 this.updatePosition(this.placement, true);
19995 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19996 var es = this.el.getSize();
19997 var x = Roo.lib.Dom.getViewWidth()/2;
19998 var y = Roo.lib.Dom.getViewHeight()/2;
19999 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20004 //var arrow = this.el.select('.arrow',true).first();
20005 //arrow.set(align[2],
20007 this.el.addClass('in');
20011 this.hoverState = 'in';
20014 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20015 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20016 this.maskEl.dom.style.display = 'block';
20017 this.maskEl.addClass('show');
20019 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20021 this.fireEvent('show', this);
20025 * fire this manually after loading a grid in the table for example
20026 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20027 * @param {Boolean} try and move it if we cant get right position.
20029 updatePosition : function(placement, try_move)
20031 // allow for calling with no parameters
20032 placement = placement ? placement : this.placement;
20033 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20035 this.el.removeClass([
20036 'fade','top','bottom', 'left', 'right','in',
20037 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20039 this.el.addClass(placement + ' bs-popover-' + placement);
20041 if (!this.alignEl ) {
20045 switch (placement) {
20047 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20048 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20049 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20050 //normal display... or moved up/down.
20051 this.el.setXY(offset);
20052 var xy = this.alignEl.getAnchorXY('tr', false);
20054 this.arrowEl.setXY(xy);
20057 // continue through...
20058 return this.updatePosition('left', false);
20062 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20063 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20064 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20065 //normal display... or moved up/down.
20066 this.el.setXY(offset);
20067 var xy = this.alignEl.getAnchorXY('tl', false);
20068 xy[0]-=10;xy[1]+=5; // << fix me
20069 this.arrowEl.setXY(xy);
20073 return this.updatePosition('right', false);
20076 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20077 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20078 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20079 //normal display... or moved up/down.
20080 this.el.setXY(offset);
20081 var xy = this.alignEl.getAnchorXY('t', false);
20082 xy[1]-=10; // << fix me
20083 this.arrowEl.setXY(xy);
20087 return this.updatePosition('bottom', false);
20090 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20091 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20092 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20093 //normal display... or moved up/down.
20094 this.el.setXY(offset);
20095 var xy = this.alignEl.getAnchorXY('b', false);
20096 xy[1]+=2; // << fix me
20097 this.arrowEl.setXY(xy);
20101 return this.updatePosition('top', false);
20112 this.el.setXY([0,0]);
20113 this.el.removeClass('in');
20115 this.hoverState = null;
20116 this.maskEl.hide(); // always..
20117 this.fireEvent('hide', this);
20123 Roo.apply(Roo.bootstrap.Popover, {
20126 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20127 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20128 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20129 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20134 clickHander : false,
20137 onMouseDown : function(e)
20139 if (!e.getTarget(".roo-popover")) {
20147 register : function(popup)
20149 if (!Roo.bootstrap.Popover.clickHandler) {
20150 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20152 // hide other popups.
20154 this.popups.push(popup);
20156 hideAll : function()
20158 this.popups.forEach(function(p) {
20166 * Card header - holder for the card header elements.
20171 * @class Roo.bootstrap.PopoverNav
20172 * @extends Roo.bootstrap.NavGroup
20173 * Bootstrap Popover header navigation class
20175 * Create a new Popover Header Navigation
20176 * @param {Object} config The config object
20179 Roo.bootstrap.PopoverNav = function(config){
20180 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20183 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20186 container_method : 'getPopoverHeader'
20204 * @class Roo.bootstrap.Progress
20205 * @extends Roo.bootstrap.Component
20206 * Bootstrap Progress class
20207 * @cfg {Boolean} striped striped of the progress bar
20208 * @cfg {Boolean} active animated of the progress bar
20212 * Create a new Progress
20213 * @param {Object} config The config object
20216 Roo.bootstrap.Progress = function(config){
20217 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20220 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20225 getAutoCreate : function(){
20233 cfg.cls += ' progress-striped';
20237 cfg.cls += ' active';
20256 * @class Roo.bootstrap.ProgressBar
20257 * @extends Roo.bootstrap.Component
20258 * Bootstrap ProgressBar class
20259 * @cfg {Number} aria_valuenow aria-value now
20260 * @cfg {Number} aria_valuemin aria-value min
20261 * @cfg {Number} aria_valuemax aria-value max
20262 * @cfg {String} label label for the progress bar
20263 * @cfg {String} panel (success | info | warning | danger )
20264 * @cfg {String} role role of the progress bar
20265 * @cfg {String} sr_only text
20269 * Create a new ProgressBar
20270 * @param {Object} config The config object
20273 Roo.bootstrap.ProgressBar = function(config){
20274 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20277 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20281 aria_valuemax : 100,
20287 getAutoCreate : function()
20292 cls: 'progress-bar',
20293 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20305 cfg.role = this.role;
20308 if(this.aria_valuenow){
20309 cfg['aria-valuenow'] = this.aria_valuenow;
20312 if(this.aria_valuemin){
20313 cfg['aria-valuemin'] = this.aria_valuemin;
20316 if(this.aria_valuemax){
20317 cfg['aria-valuemax'] = this.aria_valuemax;
20320 if(this.label && !this.sr_only){
20321 cfg.html = this.label;
20325 cfg.cls += ' progress-bar-' + this.panel;
20331 update : function(aria_valuenow)
20333 this.aria_valuenow = aria_valuenow;
20335 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20350 * @class Roo.bootstrap.TabGroup
20351 * @extends Roo.bootstrap.Column
20352 * Bootstrap Column class
20353 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20354 * @cfg {Boolean} carousel true to make the group behave like a carousel
20355 * @cfg {Boolean} bullets show bullets for the panels
20356 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20357 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20358 * @cfg {Boolean} showarrow (true|false) show arrow default true
20361 * Create a new TabGroup
20362 * @param {Object} config The config object
20365 Roo.bootstrap.TabGroup = function(config){
20366 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20368 this.navId = Roo.id();
20371 Roo.bootstrap.TabGroup.register(this);
20375 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20378 transition : false,
20383 slideOnTouch : false,
20386 getAutoCreate : function()
20388 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20390 cfg.cls += ' tab-content';
20392 if (this.carousel) {
20393 cfg.cls += ' carousel slide';
20396 cls : 'carousel-inner',
20400 if(this.bullets && !Roo.isTouch){
20403 cls : 'carousel-bullets',
20407 if(this.bullets_cls){
20408 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20415 cfg.cn[0].cn.push(bullets);
20418 if(this.showarrow){
20419 cfg.cn[0].cn.push({
20421 class : 'carousel-arrow',
20425 class : 'carousel-prev',
20429 class : 'fa fa-chevron-left'
20435 class : 'carousel-next',
20439 class : 'fa fa-chevron-right'
20452 initEvents: function()
20454 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20455 // this.el.on("touchstart", this.onTouchStart, this);
20458 if(this.autoslide){
20461 this.slideFn = window.setInterval(function() {
20462 _this.showPanelNext();
20466 if(this.showarrow){
20467 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20468 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20474 // onTouchStart : function(e, el, o)
20476 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20480 // this.showPanelNext();
20484 getChildContainer : function()
20486 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20490 * register a Navigation item
20491 * @param {Roo.bootstrap.NavItem} the navitem to add
20493 register : function(item)
20495 this.tabs.push( item);
20496 item.navId = this.navId; // not really needed..
20501 getActivePanel : function()
20504 Roo.each(this.tabs, function(t) {
20514 getPanelByName : function(n)
20517 Roo.each(this.tabs, function(t) {
20518 if (t.tabId == n) {
20526 indexOfPanel : function(p)
20529 Roo.each(this.tabs, function(t,i) {
20530 if (t.tabId == p.tabId) {
20539 * show a specific panel
20540 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20541 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20543 showPanel : function (pan)
20545 if(this.transition || typeof(pan) == 'undefined'){
20546 Roo.log("waiting for the transitionend");
20550 if (typeof(pan) == 'number') {
20551 pan = this.tabs[pan];
20554 if (typeof(pan) == 'string') {
20555 pan = this.getPanelByName(pan);
20558 var cur = this.getActivePanel();
20561 Roo.log('pan or acitve pan is undefined');
20565 if (pan.tabId == this.getActivePanel().tabId) {
20569 if (false === cur.fireEvent('beforedeactivate')) {
20573 if(this.bullets > 0 && !Roo.isTouch){
20574 this.setActiveBullet(this.indexOfPanel(pan));
20577 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20579 //class="carousel-item carousel-item-next carousel-item-left"
20581 this.transition = true;
20582 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20583 var lr = dir == 'next' ? 'left' : 'right';
20584 pan.el.addClass(dir); // or prev
20585 pan.el.addClass('carousel-item-' + dir); // or prev
20586 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20587 cur.el.addClass(lr); // or right
20588 pan.el.addClass(lr);
20589 cur.el.addClass('carousel-item-' +lr); // or right
20590 pan.el.addClass('carousel-item-' +lr);
20594 cur.el.on('transitionend', function() {
20595 Roo.log("trans end?");
20597 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20598 pan.setActive(true);
20600 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20601 cur.setActive(false);
20603 _this.transition = false;
20605 }, this, { single: true } );
20610 cur.setActive(false);
20611 pan.setActive(true);
20616 showPanelNext : function()
20618 var i = this.indexOfPanel(this.getActivePanel());
20620 if (i >= this.tabs.length - 1 && !this.autoslide) {
20624 if (i >= this.tabs.length - 1 && this.autoslide) {
20628 this.showPanel(this.tabs[i+1]);
20631 showPanelPrev : function()
20633 var i = this.indexOfPanel(this.getActivePanel());
20635 if (i < 1 && !this.autoslide) {
20639 if (i < 1 && this.autoslide) {
20640 i = this.tabs.length;
20643 this.showPanel(this.tabs[i-1]);
20647 addBullet: function()
20649 if(!this.bullets || Roo.isTouch){
20652 var ctr = this.el.select('.carousel-bullets',true).first();
20653 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20654 var bullet = ctr.createChild({
20655 cls : 'bullet bullet-' + i
20656 },ctr.dom.lastChild);
20661 bullet.on('click', (function(e, el, o, ii, t){
20663 e.preventDefault();
20665 this.showPanel(ii);
20667 if(this.autoslide && this.slideFn){
20668 clearInterval(this.slideFn);
20669 this.slideFn = window.setInterval(function() {
20670 _this.showPanelNext();
20674 }).createDelegate(this, [i, bullet], true));
20679 setActiveBullet : function(i)
20685 Roo.each(this.el.select('.bullet', true).elements, function(el){
20686 el.removeClass('selected');
20689 var bullet = this.el.select('.bullet-' + i, true).first();
20695 bullet.addClass('selected');
20706 Roo.apply(Roo.bootstrap.TabGroup, {
20710 * register a Navigation Group
20711 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20713 register : function(navgrp)
20715 this.groups[navgrp.navId] = navgrp;
20719 * fetch a Navigation Group based on the navigation ID
20720 * if one does not exist , it will get created.
20721 * @param {string} the navgroup to add
20722 * @returns {Roo.bootstrap.NavGroup} the navgroup
20724 get: function(navId) {
20725 if (typeof(this.groups[navId]) == 'undefined') {
20726 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20728 return this.groups[navId] ;
20743 * @class Roo.bootstrap.TabPanel
20744 * @extends Roo.bootstrap.Component
20745 * Bootstrap TabPanel class
20746 * @cfg {Boolean} active panel active
20747 * @cfg {String} html panel content
20748 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20749 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20750 * @cfg {String} href click to link..
20751 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20755 * Create a new TabPanel
20756 * @param {Object} config The config object
20759 Roo.bootstrap.TabPanel = function(config){
20760 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20764 * Fires when the active status changes
20765 * @param {Roo.bootstrap.TabPanel} this
20766 * @param {Boolean} state the new state
20771 * @event beforedeactivate
20772 * Fires before a tab is de-activated - can be used to do validation on a form.
20773 * @param {Roo.bootstrap.TabPanel} this
20774 * @return {Boolean} false if there is an error
20777 'beforedeactivate': true
20780 this.tabId = this.tabId || Roo.id();
20784 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20791 touchSlide : false,
20792 getAutoCreate : function(){
20797 // item is needed for carousel - not sure if it has any effect otherwise
20798 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20799 html: this.html || ''
20803 cfg.cls += ' active';
20807 cfg.tabId = this.tabId;
20815 initEvents: function()
20817 var p = this.parent();
20819 this.navId = this.navId || p.navId;
20821 if (typeof(this.navId) != 'undefined') {
20822 // not really needed.. but just in case.. parent should be a NavGroup.
20823 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20827 var i = tg.tabs.length - 1;
20829 if(this.active && tg.bullets > 0 && i < tg.bullets){
20830 tg.setActiveBullet(i);
20834 this.el.on('click', this.onClick, this);
20836 if(Roo.isTouch && this.touchSlide){
20837 this.el.on("touchstart", this.onTouchStart, this);
20838 this.el.on("touchmove", this.onTouchMove, this);
20839 this.el.on("touchend", this.onTouchEnd, this);
20844 onRender : function(ct, position)
20846 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20849 setActive : function(state)
20851 Roo.log("panel - set active " + this.tabId + "=" + state);
20853 this.active = state;
20855 this.el.removeClass('active');
20857 } else if (!this.el.hasClass('active')) {
20858 this.el.addClass('active');
20861 this.fireEvent('changed', this, state);
20864 onClick : function(e)
20866 e.preventDefault();
20868 if(!this.href.length){
20872 window.location.href = this.href;
20881 onTouchStart : function(e)
20883 this.swiping = false;
20885 this.startX = e.browserEvent.touches[0].clientX;
20886 this.startY = e.browserEvent.touches[0].clientY;
20889 onTouchMove : function(e)
20891 this.swiping = true;
20893 this.endX = e.browserEvent.touches[0].clientX;
20894 this.endY = e.browserEvent.touches[0].clientY;
20897 onTouchEnd : function(e)
20904 var tabGroup = this.parent();
20906 if(this.endX > this.startX){ // swiping right
20907 tabGroup.showPanelPrev();
20911 if(this.startX > this.endX){ // swiping left
20912 tabGroup.showPanelNext();
20931 * @class Roo.bootstrap.DateField
20932 * @extends Roo.bootstrap.Input
20933 * Bootstrap DateField class
20934 * @cfg {Number} weekStart default 0
20935 * @cfg {String} viewMode default empty, (months|years)
20936 * @cfg {String} minViewMode default empty, (months|years)
20937 * @cfg {Number} startDate default -Infinity
20938 * @cfg {Number} endDate default Infinity
20939 * @cfg {Boolean} todayHighlight default false
20940 * @cfg {Boolean} todayBtn default false
20941 * @cfg {Boolean} calendarWeeks default false
20942 * @cfg {Object} daysOfWeekDisabled default empty
20943 * @cfg {Boolean} singleMode default false (true | false)
20945 * @cfg {Boolean} keyboardNavigation default true
20946 * @cfg {String} language default en
20949 * Create a new DateField
20950 * @param {Object} config The config object
20953 Roo.bootstrap.DateField = function(config){
20954 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20958 * Fires when this field show.
20959 * @param {Roo.bootstrap.DateField} this
20960 * @param {Mixed} date The date value
20965 * Fires when this field hide.
20966 * @param {Roo.bootstrap.DateField} this
20967 * @param {Mixed} date The date value
20972 * Fires when select a date.
20973 * @param {Roo.bootstrap.DateField} this
20974 * @param {Mixed} date The date value
20978 * @event beforeselect
20979 * Fires when before select a date.
20980 * @param {Roo.bootstrap.DateField} this
20981 * @param {Mixed} date The date value
20983 beforeselect : true
20987 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20990 * @cfg {String} format
20991 * The default date format string which can be overriden for localization support. The format must be
20992 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20996 * @cfg {String} altFormats
20997 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20998 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21000 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21008 todayHighlight : false,
21014 keyboardNavigation: true,
21016 calendarWeeks: false,
21018 startDate: -Infinity,
21022 daysOfWeekDisabled: [],
21026 singleMode : false,
21028 UTCDate: function()
21030 return new Date(Date.UTC.apply(Date, arguments));
21033 UTCToday: function()
21035 var today = new Date();
21036 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21039 getDate: function() {
21040 var d = this.getUTCDate();
21041 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21044 getUTCDate: function() {
21048 setDate: function(d) {
21049 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21052 setUTCDate: function(d) {
21054 this.setValue(this.formatDate(this.date));
21057 onRender: function(ct, position)
21060 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21062 this.language = this.language || 'en';
21063 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21064 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21066 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21067 this.format = this.format || 'm/d/y';
21068 this.isInline = false;
21069 this.isInput = true;
21070 this.component = this.el.select('.add-on', true).first() || false;
21071 this.component = (this.component && this.component.length === 0) ? false : this.component;
21072 this.hasInput = this.component && this.inputEl().length;
21074 if (typeof(this.minViewMode === 'string')) {
21075 switch (this.minViewMode) {
21077 this.minViewMode = 1;
21080 this.minViewMode = 2;
21083 this.minViewMode = 0;
21088 if (typeof(this.viewMode === 'string')) {
21089 switch (this.viewMode) {
21102 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21104 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21106 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21108 this.picker().on('mousedown', this.onMousedown, this);
21109 this.picker().on('click', this.onClick, this);
21111 this.picker().addClass('datepicker-dropdown');
21113 this.startViewMode = this.viewMode;
21115 if(this.singleMode){
21116 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21117 v.setVisibilityMode(Roo.Element.DISPLAY);
21121 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21122 v.setStyle('width', '189px');
21126 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21127 if(!this.calendarWeeks){
21132 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21133 v.attr('colspan', function(i, val){
21134 return parseInt(val) + 1;
21139 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21141 this.setStartDate(this.startDate);
21142 this.setEndDate(this.endDate);
21144 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21151 if(this.isInline) {
21156 picker : function()
21158 return this.pickerEl;
21159 // return this.el.select('.datepicker', true).first();
21162 fillDow: function()
21164 var dowCnt = this.weekStart;
21173 if(this.calendarWeeks){
21181 while (dowCnt < this.weekStart + 7) {
21185 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21189 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21192 fillMonths: function()
21195 var months = this.picker().select('>.datepicker-months td', true).first();
21197 months.dom.innerHTML = '';
21203 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21206 months.createChild(month);
21213 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;
21215 if (this.date < this.startDate) {
21216 this.viewDate = new Date(this.startDate);
21217 } else if (this.date > this.endDate) {
21218 this.viewDate = new Date(this.endDate);
21220 this.viewDate = new Date(this.date);
21228 var d = new Date(this.viewDate),
21229 year = d.getUTCFullYear(),
21230 month = d.getUTCMonth(),
21231 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21232 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21233 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21234 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21235 currentDate = this.date && this.date.valueOf(),
21236 today = this.UTCToday();
21238 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21240 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21242 // this.picker.select('>tfoot th.today').
21243 // .text(dates[this.language].today)
21244 // .toggle(this.todayBtn !== false);
21246 this.updateNavArrows();
21249 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21251 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21253 prevMonth.setUTCDate(day);
21255 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21257 var nextMonth = new Date(prevMonth);
21259 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21261 nextMonth = nextMonth.valueOf();
21263 var fillMonths = false;
21265 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21267 while(prevMonth.valueOf() <= nextMonth) {
21270 if (prevMonth.getUTCDay() === this.weekStart) {
21272 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21280 if(this.calendarWeeks){
21281 // ISO 8601: First week contains first thursday.
21282 // ISO also states week starts on Monday, but we can be more abstract here.
21284 // Start of current week: based on weekstart/current date
21285 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21286 // Thursday of this week
21287 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21288 // First Thursday of year, year from thursday
21289 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21290 // Calendar week: ms between thursdays, div ms per day, div 7 days
21291 calWeek = (th - yth) / 864e5 / 7 + 1;
21293 fillMonths.cn.push({
21301 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21303 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21306 if (this.todayHighlight &&
21307 prevMonth.getUTCFullYear() == today.getFullYear() &&
21308 prevMonth.getUTCMonth() == today.getMonth() &&
21309 prevMonth.getUTCDate() == today.getDate()) {
21310 clsName += ' today';
21313 if (currentDate && prevMonth.valueOf() === currentDate) {
21314 clsName += ' active';
21317 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21318 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21319 clsName += ' disabled';
21322 fillMonths.cn.push({
21324 cls: 'day ' + clsName,
21325 html: prevMonth.getDate()
21328 prevMonth.setDate(prevMonth.getDate()+1);
21331 var currentYear = this.date && this.date.getUTCFullYear();
21332 var currentMonth = this.date && this.date.getUTCMonth();
21334 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21336 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21337 v.removeClass('active');
21339 if(currentYear === year && k === currentMonth){
21340 v.addClass('active');
21343 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21344 v.addClass('disabled');
21350 year = parseInt(year/10, 10) * 10;
21352 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21354 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21357 for (var i = -1; i < 11; i++) {
21358 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21360 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21368 showMode: function(dir)
21371 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21374 Roo.each(this.picker().select('>div',true).elements, function(v){
21375 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21378 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21383 if(this.isInline) {
21387 this.picker().removeClass(['bottom', 'top']);
21389 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21391 * place to the top of element!
21395 this.picker().addClass('top');
21396 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21401 this.picker().addClass('bottom');
21403 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21406 parseDate : function(value)
21408 if(!value || value instanceof Date){
21411 var v = Date.parseDate(value, this.format);
21412 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21413 v = Date.parseDate(value, 'Y-m-d');
21415 if(!v && this.altFormats){
21416 if(!this.altFormatsArray){
21417 this.altFormatsArray = this.altFormats.split("|");
21419 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21420 v = Date.parseDate(value, this.altFormatsArray[i]);
21426 formatDate : function(date, fmt)
21428 return (!date || !(date instanceof Date)) ?
21429 date : date.dateFormat(fmt || this.format);
21432 onFocus : function()
21434 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21438 onBlur : function()
21440 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21442 var d = this.inputEl().getValue();
21449 showPopup : function()
21451 this.picker().show();
21455 this.fireEvent('showpopup', this, this.date);
21458 hidePopup : function()
21460 if(this.isInline) {
21463 this.picker().hide();
21464 this.viewMode = this.startViewMode;
21467 this.fireEvent('hidepopup', this, this.date);
21471 onMousedown: function(e)
21473 e.stopPropagation();
21474 e.preventDefault();
21479 Roo.bootstrap.DateField.superclass.keyup.call(this);
21483 setValue: function(v)
21485 if(this.fireEvent('beforeselect', this, v) !== false){
21486 var d = new Date(this.parseDate(v) ).clearTime();
21488 if(isNaN(d.getTime())){
21489 this.date = this.viewDate = '';
21490 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21494 v = this.formatDate(d);
21496 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21498 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21502 this.fireEvent('select', this, this.date);
21506 getValue: function()
21508 return this.formatDate(this.date);
21511 fireKey: function(e)
21513 if (!this.picker().isVisible()){
21514 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21520 var dateChanged = false,
21522 newDate, newViewDate;
21527 e.preventDefault();
21531 if (!this.keyboardNavigation) {
21534 dir = e.keyCode == 37 ? -1 : 1;
21537 newDate = this.moveYear(this.date, dir);
21538 newViewDate = this.moveYear(this.viewDate, dir);
21539 } else if (e.shiftKey){
21540 newDate = this.moveMonth(this.date, dir);
21541 newViewDate = this.moveMonth(this.viewDate, dir);
21543 newDate = new Date(this.date);
21544 newDate.setUTCDate(this.date.getUTCDate() + dir);
21545 newViewDate = new Date(this.viewDate);
21546 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21548 if (this.dateWithinRange(newDate)){
21549 this.date = newDate;
21550 this.viewDate = newViewDate;
21551 this.setValue(this.formatDate(this.date));
21553 e.preventDefault();
21554 dateChanged = true;
21559 if (!this.keyboardNavigation) {
21562 dir = e.keyCode == 38 ? -1 : 1;
21564 newDate = this.moveYear(this.date, dir);
21565 newViewDate = this.moveYear(this.viewDate, dir);
21566 } else if (e.shiftKey){
21567 newDate = this.moveMonth(this.date, dir);
21568 newViewDate = this.moveMonth(this.viewDate, dir);
21570 newDate = new Date(this.date);
21571 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21572 newViewDate = new Date(this.viewDate);
21573 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21575 if (this.dateWithinRange(newDate)){
21576 this.date = newDate;
21577 this.viewDate = newViewDate;
21578 this.setValue(this.formatDate(this.date));
21580 e.preventDefault();
21581 dateChanged = true;
21585 this.setValue(this.formatDate(this.date));
21587 e.preventDefault();
21590 this.setValue(this.formatDate(this.date));
21604 onClick: function(e)
21606 e.stopPropagation();
21607 e.preventDefault();
21609 var target = e.getTarget();
21611 if(target.nodeName.toLowerCase() === 'i'){
21612 target = Roo.get(target).dom.parentNode;
21615 var nodeName = target.nodeName;
21616 var className = target.className;
21617 var html = target.innerHTML;
21618 //Roo.log(nodeName);
21620 switch(nodeName.toLowerCase()) {
21622 switch(className) {
21628 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21629 switch(this.viewMode){
21631 this.viewDate = this.moveMonth(this.viewDate, dir);
21635 this.viewDate = this.moveYear(this.viewDate, dir);
21641 var date = new Date();
21642 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21644 this.setValue(this.formatDate(this.date));
21651 if (className.indexOf('disabled') < 0) {
21652 this.viewDate.setUTCDate(1);
21653 if (className.indexOf('month') > -1) {
21654 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21656 var year = parseInt(html, 10) || 0;
21657 this.viewDate.setUTCFullYear(year);
21661 if(this.singleMode){
21662 this.setValue(this.formatDate(this.viewDate));
21673 //Roo.log(className);
21674 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21675 var day = parseInt(html, 10) || 1;
21676 var year = (this.viewDate || new Date()).getUTCFullYear(),
21677 month = (this.viewDate || new Date()).getUTCMonth();
21679 if (className.indexOf('old') > -1) {
21686 } else if (className.indexOf('new') > -1) {
21694 //Roo.log([year,month,day]);
21695 this.date = this.UTCDate(year, month, day,0,0,0,0);
21696 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21698 //Roo.log(this.formatDate(this.date));
21699 this.setValue(this.formatDate(this.date));
21706 setStartDate: function(startDate)
21708 this.startDate = startDate || -Infinity;
21709 if (this.startDate !== -Infinity) {
21710 this.startDate = this.parseDate(this.startDate);
21713 this.updateNavArrows();
21716 setEndDate: function(endDate)
21718 this.endDate = endDate || Infinity;
21719 if (this.endDate !== Infinity) {
21720 this.endDate = this.parseDate(this.endDate);
21723 this.updateNavArrows();
21726 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21728 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21729 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21730 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21732 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21733 return parseInt(d, 10);
21736 this.updateNavArrows();
21739 updateNavArrows: function()
21741 if(this.singleMode){
21745 var d = new Date(this.viewDate),
21746 year = d.getUTCFullYear(),
21747 month = d.getUTCMonth();
21749 Roo.each(this.picker().select('.prev', true).elements, function(v){
21751 switch (this.viewMode) {
21754 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21760 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21767 Roo.each(this.picker().select('.next', true).elements, function(v){
21769 switch (this.viewMode) {
21772 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21778 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21786 moveMonth: function(date, dir)
21791 var new_date = new Date(date.valueOf()),
21792 day = new_date.getUTCDate(),
21793 month = new_date.getUTCMonth(),
21794 mag = Math.abs(dir),
21796 dir = dir > 0 ? 1 : -1;
21799 // If going back one month, make sure month is not current month
21800 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21802 return new_date.getUTCMonth() == month;
21804 // If going forward one month, make sure month is as expected
21805 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21807 return new_date.getUTCMonth() != new_month;
21809 new_month = month + dir;
21810 new_date.setUTCMonth(new_month);
21811 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21812 if (new_month < 0 || new_month > 11) {
21813 new_month = (new_month + 12) % 12;
21816 // For magnitudes >1, move one month at a time...
21817 for (var i=0; i<mag; i++) {
21818 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21819 new_date = this.moveMonth(new_date, dir);
21821 // ...then reset the day, keeping it in the new month
21822 new_month = new_date.getUTCMonth();
21823 new_date.setUTCDate(day);
21825 return new_month != new_date.getUTCMonth();
21828 // Common date-resetting loop -- if date is beyond end of month, make it
21831 new_date.setUTCDate(--day);
21832 new_date.setUTCMonth(new_month);
21837 moveYear: function(date, dir)
21839 return this.moveMonth(date, dir*12);
21842 dateWithinRange: function(date)
21844 return date >= this.startDate && date <= this.endDate;
21850 this.picker().remove();
21853 validateValue : function(value)
21855 if(this.getVisibilityEl().hasClass('hidden')){
21859 if(value.length < 1) {
21860 if(this.allowBlank){
21866 if(value.length < this.minLength){
21869 if(value.length > this.maxLength){
21873 var vt = Roo.form.VTypes;
21874 if(!vt[this.vtype](value, this)){
21878 if(typeof this.validator == "function"){
21879 var msg = this.validator(value);
21885 if(this.regex && !this.regex.test(value)){
21889 if(typeof(this.parseDate(value)) == 'undefined'){
21893 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21897 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21907 this.date = this.viewDate = '';
21909 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21914 Roo.apply(Roo.bootstrap.DateField, {
21925 html: '<i class="fa fa-arrow-left"/>'
21935 html: '<i class="fa fa-arrow-right"/>'
21977 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21978 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21979 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21980 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21981 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21994 navFnc: 'FullYear',
21999 navFnc: 'FullYear',
22004 Roo.apply(Roo.bootstrap.DateField, {
22008 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22012 cls: 'datepicker-days',
22016 cls: 'table-condensed',
22018 Roo.bootstrap.DateField.head,
22022 Roo.bootstrap.DateField.footer
22029 cls: 'datepicker-months',
22033 cls: 'table-condensed',
22035 Roo.bootstrap.DateField.head,
22036 Roo.bootstrap.DateField.content,
22037 Roo.bootstrap.DateField.footer
22044 cls: 'datepicker-years',
22048 cls: 'table-condensed',
22050 Roo.bootstrap.DateField.head,
22051 Roo.bootstrap.DateField.content,
22052 Roo.bootstrap.DateField.footer
22071 * @class Roo.bootstrap.TimeField
22072 * @extends Roo.bootstrap.Input
22073 * Bootstrap DateField class
22077 * Create a new TimeField
22078 * @param {Object} config The config object
22081 Roo.bootstrap.TimeField = function(config){
22082 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22086 * Fires when this field show.
22087 * @param {Roo.bootstrap.DateField} thisthis
22088 * @param {Mixed} date The date value
22093 * Fires when this field hide.
22094 * @param {Roo.bootstrap.DateField} this
22095 * @param {Mixed} date The date value
22100 * Fires when select a date.
22101 * @param {Roo.bootstrap.DateField} this
22102 * @param {Mixed} date The date value
22108 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22111 * @cfg {String} format
22112 * The default time format string which can be overriden for localization support. The format must be
22113 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22117 getAutoCreate : function()
22119 this.after = '<i class="fa far fa-clock"></i>';
22120 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22124 onRender: function(ct, position)
22127 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22129 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22131 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22133 this.pop = this.picker().select('>.datepicker-time',true).first();
22134 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22136 this.picker().on('mousedown', this.onMousedown, this);
22137 this.picker().on('click', this.onClick, this);
22139 this.picker().addClass('datepicker-dropdown');
22144 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22145 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22146 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22147 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22148 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22149 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22153 fireKey: function(e){
22154 if (!this.picker().isVisible()){
22155 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22161 e.preventDefault();
22169 this.onTogglePeriod();
22172 this.onIncrementMinutes();
22175 this.onDecrementMinutes();
22184 onClick: function(e) {
22185 e.stopPropagation();
22186 e.preventDefault();
22189 picker : function()
22191 return this.pickerEl;
22194 fillTime: function()
22196 var time = this.pop.select('tbody', true).first();
22198 time.dom.innerHTML = '';
22213 cls: 'hours-up fa fas fa-chevron-up'
22233 cls: 'minutes-up fa fas fa-chevron-up'
22254 cls: 'timepicker-hour',
22269 cls: 'timepicker-minute',
22284 cls: 'btn btn-primary period',
22306 cls: 'hours-down fa fas fa-chevron-down'
22326 cls: 'minutes-down fa fas fa-chevron-down'
22344 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22351 var hours = this.time.getHours();
22352 var minutes = this.time.getMinutes();
22365 hours = hours - 12;
22369 hours = '0' + hours;
22373 minutes = '0' + minutes;
22376 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22377 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22378 this.pop.select('button', true).first().dom.innerHTML = period;
22384 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22386 var cls = ['bottom'];
22388 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22395 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22399 //this.picker().setXY(20000,20000);
22400 this.picker().addClass(cls.join('-'));
22404 Roo.each(cls, function(c){
22409 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22410 //_this.picker().setTop(_this.inputEl().getHeight());
22414 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22416 //_this.picker().setTop(0 - _this.picker().getHeight());
22421 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22425 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22433 onFocus : function()
22435 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22439 onBlur : function()
22441 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22447 this.picker().show();
22452 this.fireEvent('show', this, this.date);
22457 this.picker().hide();
22460 this.fireEvent('hide', this, this.date);
22463 setTime : function()
22466 this.setValue(this.time.format(this.format));
22468 this.fireEvent('select', this, this.date);
22473 onMousedown: function(e){
22474 e.stopPropagation();
22475 e.preventDefault();
22478 onIncrementHours: function()
22480 Roo.log('onIncrementHours');
22481 this.time = this.time.add(Date.HOUR, 1);
22486 onDecrementHours: function()
22488 Roo.log('onDecrementHours');
22489 this.time = this.time.add(Date.HOUR, -1);
22493 onIncrementMinutes: function()
22495 Roo.log('onIncrementMinutes');
22496 this.time = this.time.add(Date.MINUTE, 1);
22500 onDecrementMinutes: function()
22502 Roo.log('onDecrementMinutes');
22503 this.time = this.time.add(Date.MINUTE, -1);
22507 onTogglePeriod: function()
22509 Roo.log('onTogglePeriod');
22510 this.time = this.time.add(Date.HOUR, 12);
22518 Roo.apply(Roo.bootstrap.TimeField, {
22522 cls: 'datepicker dropdown-menu',
22526 cls: 'datepicker-time',
22530 cls: 'table-condensed',
22559 cls: 'btn btn-info ok',
22587 * @class Roo.bootstrap.MonthField
22588 * @extends Roo.bootstrap.Input
22589 * Bootstrap MonthField class
22591 * @cfg {String} language default en
22594 * Create a new MonthField
22595 * @param {Object} config The config object
22598 Roo.bootstrap.MonthField = function(config){
22599 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22604 * Fires when this field show.
22605 * @param {Roo.bootstrap.MonthField} this
22606 * @param {Mixed} date The date value
22611 * Fires when this field hide.
22612 * @param {Roo.bootstrap.MonthField} this
22613 * @param {Mixed} date The date value
22618 * Fires when select a date.
22619 * @param {Roo.bootstrap.MonthField} this
22620 * @param {String} oldvalue The old value
22621 * @param {String} newvalue The new value
22627 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22629 onRender: function(ct, position)
22632 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22634 this.language = this.language || 'en';
22635 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22636 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22638 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22639 this.isInline = false;
22640 this.isInput = true;
22641 this.component = this.el.select('.add-on', true).first() || false;
22642 this.component = (this.component && this.component.length === 0) ? false : this.component;
22643 this.hasInput = this.component && this.inputEL().length;
22645 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22647 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22649 this.picker().on('mousedown', this.onMousedown, this);
22650 this.picker().on('click', this.onClick, this);
22652 this.picker().addClass('datepicker-dropdown');
22654 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22655 v.setStyle('width', '189px');
22662 if(this.isInline) {
22668 setValue: function(v, suppressEvent)
22670 var o = this.getValue();
22672 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22676 if(suppressEvent !== true){
22677 this.fireEvent('select', this, o, v);
22682 getValue: function()
22687 onClick: function(e)
22689 e.stopPropagation();
22690 e.preventDefault();
22692 var target = e.getTarget();
22694 if(target.nodeName.toLowerCase() === 'i'){
22695 target = Roo.get(target).dom.parentNode;
22698 var nodeName = target.nodeName;
22699 var className = target.className;
22700 var html = target.innerHTML;
22702 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22706 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22708 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22714 picker : function()
22716 return this.pickerEl;
22719 fillMonths: function()
22722 var months = this.picker().select('>.datepicker-months td', true).first();
22724 months.dom.innerHTML = '';
22730 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22733 months.createChild(month);
22742 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22743 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22746 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22747 e.removeClass('active');
22749 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22750 e.addClass('active');
22757 if(this.isInline) {
22761 this.picker().removeClass(['bottom', 'top']);
22763 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22765 * place to the top of element!
22769 this.picker().addClass('top');
22770 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22775 this.picker().addClass('bottom');
22777 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22780 onFocus : function()
22782 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22786 onBlur : function()
22788 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22790 var d = this.inputEl().getValue();
22799 this.picker().show();
22800 this.picker().select('>.datepicker-months', true).first().show();
22804 this.fireEvent('show', this, this.date);
22809 if(this.isInline) {
22812 this.picker().hide();
22813 this.fireEvent('hide', this, this.date);
22817 onMousedown: function(e)
22819 e.stopPropagation();
22820 e.preventDefault();
22825 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22829 fireKey: function(e)
22831 if (!this.picker().isVisible()){
22832 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22843 e.preventDefault();
22847 dir = e.keyCode == 37 ? -1 : 1;
22849 this.vIndex = this.vIndex + dir;
22851 if(this.vIndex < 0){
22855 if(this.vIndex > 11){
22859 if(isNaN(this.vIndex)){
22863 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22869 dir = e.keyCode == 38 ? -1 : 1;
22871 this.vIndex = this.vIndex + dir * 4;
22873 if(this.vIndex < 0){
22877 if(this.vIndex > 11){
22881 if(isNaN(this.vIndex)){
22885 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22890 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22891 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22895 e.preventDefault();
22898 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22899 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22915 this.picker().remove();
22920 Roo.apply(Roo.bootstrap.MonthField, {
22939 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22940 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22945 Roo.apply(Roo.bootstrap.MonthField, {
22949 cls: 'datepicker dropdown-menu roo-dynamic',
22953 cls: 'datepicker-months',
22957 cls: 'table-condensed',
22959 Roo.bootstrap.DateField.content
22979 * @class Roo.bootstrap.CheckBox
22980 * @extends Roo.bootstrap.Input
22981 * Bootstrap CheckBox class
22983 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22984 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22985 * @cfg {String} boxLabel The text that appears beside the checkbox
22986 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22987 * @cfg {Boolean} checked initnal the element
22988 * @cfg {Boolean} inline inline the element (default false)
22989 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22990 * @cfg {String} tooltip label tooltip
22993 * Create a new CheckBox
22994 * @param {Object} config The config object
22997 Roo.bootstrap.CheckBox = function(config){
22998 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23003 * Fires when the element is checked or unchecked.
23004 * @param {Roo.bootstrap.CheckBox} this This input
23005 * @param {Boolean} checked The new checked value
23010 * Fires when the element is click.
23011 * @param {Roo.bootstrap.CheckBox} this This input
23018 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23020 inputType: 'checkbox',
23029 // checkbox success does not make any sense really..
23034 getAutoCreate : function()
23036 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23042 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23045 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23051 type : this.inputType,
23052 value : this.inputValue,
23053 cls : 'roo-' + this.inputType, //'form-box',
23054 placeholder : this.placeholder || ''
23058 if(this.inputType != 'radio'){
23062 cls : 'roo-hidden-value',
23063 value : this.checked ? this.inputValue : this.valueOff
23068 if (this.weight) { // Validity check?
23069 cfg.cls += " " + this.inputType + "-" + this.weight;
23072 if (this.disabled) {
23073 input.disabled=true;
23077 input.checked = this.checked;
23082 input.name = this.name;
23084 if(this.inputType != 'radio'){
23085 hidden.name = this.name;
23086 input.name = '_hidden_' + this.name;
23091 input.cls += ' input-' + this.size;
23096 ['xs','sm','md','lg'].map(function(size){
23097 if (settings[size]) {
23098 cfg.cls += ' col-' + size + '-' + settings[size];
23102 var inputblock = input;
23104 if (this.before || this.after) {
23107 cls : 'input-group',
23112 inputblock.cn.push({
23114 cls : 'input-group-addon',
23119 inputblock.cn.push(input);
23121 if(this.inputType != 'radio'){
23122 inputblock.cn.push(hidden);
23126 inputblock.cn.push({
23128 cls : 'input-group-addon',
23134 var boxLabelCfg = false;
23140 //'for': id, // box label is handled by onclick - so no for...
23142 html: this.boxLabel
23145 boxLabelCfg.tooltip = this.tooltip;
23151 if (align ==='left' && this.fieldLabel.length) {
23152 // Roo.log("left and has label");
23157 cls : 'control-label',
23158 html : this.fieldLabel
23169 cfg.cn[1].cn.push(boxLabelCfg);
23172 if(this.labelWidth > 12){
23173 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23176 if(this.labelWidth < 13 && this.labelmd == 0){
23177 this.labelmd = this.labelWidth;
23180 if(this.labellg > 0){
23181 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23182 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23185 if(this.labelmd > 0){
23186 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23187 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23190 if(this.labelsm > 0){
23191 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23192 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23195 if(this.labelxs > 0){
23196 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23197 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23200 } else if ( this.fieldLabel.length) {
23201 // Roo.log(" label");
23205 tag: this.boxLabel ? 'span' : 'label',
23207 cls: 'control-label box-input-label',
23208 //cls : 'input-group-addon',
23209 html : this.fieldLabel
23216 cfg.cn.push(boxLabelCfg);
23221 // Roo.log(" no label && no align");
23222 cfg.cn = [ inputblock ] ;
23224 cfg.cn.push(boxLabelCfg);
23232 if(this.inputType != 'radio'){
23233 cfg.cn.push(hidden);
23241 * return the real input element.
23243 inputEl: function ()
23245 return this.el.select('input.roo-' + this.inputType,true).first();
23247 hiddenEl: function ()
23249 return this.el.select('input.roo-hidden-value',true).first();
23252 labelEl: function()
23254 return this.el.select('label.control-label',true).first();
23256 /* depricated... */
23260 return this.labelEl();
23263 boxLabelEl: function()
23265 return this.el.select('label.box-label',true).first();
23268 initEvents : function()
23270 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23272 this.inputEl().on('click', this.onClick, this);
23274 if (this.boxLabel) {
23275 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23278 this.startValue = this.getValue();
23281 Roo.bootstrap.CheckBox.register(this);
23285 onClick : function(e)
23287 if(this.fireEvent('click', this, e) !== false){
23288 this.setChecked(!this.checked);
23293 setChecked : function(state,suppressEvent)
23295 this.startValue = this.getValue();
23297 if(this.inputType == 'radio'){
23299 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23300 e.dom.checked = false;
23303 this.inputEl().dom.checked = true;
23305 this.inputEl().dom.value = this.inputValue;
23307 if(suppressEvent !== true){
23308 this.fireEvent('check', this, true);
23316 this.checked = state;
23318 this.inputEl().dom.checked = state;
23321 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23323 if(suppressEvent !== true){
23324 this.fireEvent('check', this, state);
23330 getValue : function()
23332 if(this.inputType == 'radio'){
23333 return this.getGroupValue();
23336 return this.hiddenEl().dom.value;
23340 getGroupValue : function()
23342 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23346 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23349 setValue : function(v,suppressEvent)
23351 if(this.inputType == 'radio'){
23352 this.setGroupValue(v, suppressEvent);
23356 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23361 setGroupValue : function(v, suppressEvent)
23363 this.startValue = this.getValue();
23365 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23366 e.dom.checked = false;
23368 if(e.dom.value == v){
23369 e.dom.checked = true;
23373 if(suppressEvent !== true){
23374 this.fireEvent('check', this, true);
23382 validate : function()
23384 if(this.getVisibilityEl().hasClass('hidden')){
23390 (this.inputType == 'radio' && this.validateRadio()) ||
23391 (this.inputType == 'checkbox' && this.validateCheckbox())
23397 this.markInvalid();
23401 validateRadio : function()
23403 if(this.getVisibilityEl().hasClass('hidden')){
23407 if(this.allowBlank){
23413 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23414 if(!e.dom.checked){
23426 validateCheckbox : function()
23429 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23430 //return (this.getValue() == this.inputValue) ? true : false;
23433 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23441 for(var i in group){
23442 if(group[i].el.isVisible(true)){
23450 for(var i in group){
23455 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23462 * Mark this field as valid
23464 markValid : function()
23468 this.fireEvent('valid', this);
23470 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23473 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23480 if(this.inputType == 'radio'){
23481 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23482 var fg = e.findParent('.form-group', false, true);
23483 if (Roo.bootstrap.version == 3) {
23484 fg.removeClass([_this.invalidClass, _this.validClass]);
23485 fg.addClass(_this.validClass);
23487 fg.removeClass(['is-valid', 'is-invalid']);
23488 fg.addClass('is-valid');
23496 var fg = this.el.findParent('.form-group', false, true);
23497 if (Roo.bootstrap.version == 3) {
23498 fg.removeClass([this.invalidClass, this.validClass]);
23499 fg.addClass(this.validClass);
23501 fg.removeClass(['is-valid', 'is-invalid']);
23502 fg.addClass('is-valid');
23507 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23513 for(var i in group){
23514 var fg = group[i].el.findParent('.form-group', false, true);
23515 if (Roo.bootstrap.version == 3) {
23516 fg.removeClass([this.invalidClass, this.validClass]);
23517 fg.addClass(this.validClass);
23519 fg.removeClass(['is-valid', 'is-invalid']);
23520 fg.addClass('is-valid');
23526 * Mark this field as invalid
23527 * @param {String} msg The validation message
23529 markInvalid : function(msg)
23531 if(this.allowBlank){
23537 this.fireEvent('invalid', this, msg);
23539 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23542 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23546 label.markInvalid();
23549 if(this.inputType == 'radio'){
23551 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23552 var fg = e.findParent('.form-group', false, true);
23553 if (Roo.bootstrap.version == 3) {
23554 fg.removeClass([_this.invalidClass, _this.validClass]);
23555 fg.addClass(_this.invalidClass);
23557 fg.removeClass(['is-invalid', 'is-valid']);
23558 fg.addClass('is-invalid');
23566 var fg = this.el.findParent('.form-group', false, true);
23567 if (Roo.bootstrap.version == 3) {
23568 fg.removeClass([_this.invalidClass, _this.validClass]);
23569 fg.addClass(_this.invalidClass);
23571 fg.removeClass(['is-invalid', 'is-valid']);
23572 fg.addClass('is-invalid');
23577 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23583 for(var i in group){
23584 var fg = group[i].el.findParent('.form-group', false, true);
23585 if (Roo.bootstrap.version == 3) {
23586 fg.removeClass([_this.invalidClass, _this.validClass]);
23587 fg.addClass(_this.invalidClass);
23589 fg.removeClass(['is-invalid', 'is-valid']);
23590 fg.addClass('is-invalid');
23596 clearInvalid : function()
23598 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23600 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23602 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23604 if (label && label.iconEl) {
23605 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23606 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23610 disable : function()
23612 if(this.inputType != 'radio'){
23613 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23620 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23621 _this.getActionEl().addClass(this.disabledClass);
23622 e.dom.disabled = true;
23626 this.disabled = true;
23627 this.fireEvent("disable", this);
23631 enable : function()
23633 if(this.inputType != 'radio'){
23634 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23641 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23642 _this.getActionEl().removeClass(this.disabledClass);
23643 e.dom.disabled = false;
23647 this.disabled = false;
23648 this.fireEvent("enable", this);
23652 setBoxLabel : function(v)
23657 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23663 Roo.apply(Roo.bootstrap.CheckBox, {
23668 * register a CheckBox Group
23669 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23671 register : function(checkbox)
23673 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23674 this.groups[checkbox.groupId] = {};
23677 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23681 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23685 * fetch a CheckBox Group based on the group ID
23686 * @param {string} the group ID
23687 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23689 get: function(groupId) {
23690 if (typeof(this.groups[groupId]) == 'undefined') {
23694 return this.groups[groupId] ;
23707 * @class Roo.bootstrap.Radio
23708 * @extends Roo.bootstrap.Component
23709 * Bootstrap Radio class
23710 * @cfg {String} boxLabel - the label associated
23711 * @cfg {String} value - the value of radio
23714 * Create a new Radio
23715 * @param {Object} config The config object
23717 Roo.bootstrap.Radio = function(config){
23718 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23722 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23728 getAutoCreate : function()
23732 cls : 'form-group radio',
23737 html : this.boxLabel
23745 initEvents : function()
23747 this.parent().register(this);
23749 this.el.on('click', this.onClick, this);
23753 onClick : function(e)
23755 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23756 this.setChecked(true);
23760 setChecked : function(state, suppressEvent)
23762 this.parent().setValue(this.value, suppressEvent);
23766 setBoxLabel : function(v)
23771 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23786 * @class Roo.bootstrap.SecurePass
23787 * @extends Roo.bootstrap.Input
23788 * Bootstrap SecurePass class
23792 * Create a new SecurePass
23793 * @param {Object} config The config object
23796 Roo.bootstrap.SecurePass = function (config) {
23797 // these go here, so the translation tool can replace them..
23799 PwdEmpty: "Please type a password, and then retype it to confirm.",
23800 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23801 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23802 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23803 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23804 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23805 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23806 TooWeak: "Your password is Too Weak."
23808 this.meterLabel = "Password strength:";
23809 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23810 this.meterClass = [
23811 "roo-password-meter-tooweak",
23812 "roo-password-meter-weak",
23813 "roo-password-meter-medium",
23814 "roo-password-meter-strong",
23815 "roo-password-meter-grey"
23820 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23823 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23825 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23827 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23828 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23829 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23830 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23831 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23832 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23833 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23843 * @cfg {String/Object} Label for the strength meter (defaults to
23844 * 'Password strength:')
23849 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23850 * ['Weak', 'Medium', 'Strong'])
23853 pwdStrengths: false,
23866 initEvents: function ()
23868 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23870 if (this.el.is('input[type=password]') && Roo.isSafari) {
23871 this.el.on('keydown', this.SafariOnKeyDown, this);
23874 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23877 onRender: function (ct, position)
23879 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23880 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23881 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23883 this.trigger.createChild({
23888 cls: 'roo-password-meter-grey col-xs-12',
23891 //width: this.meterWidth + 'px'
23895 cls: 'roo-password-meter-text'
23901 if (this.hideTrigger) {
23902 this.trigger.setDisplayed(false);
23904 this.setSize(this.width || '', this.height || '');
23907 onDestroy: function ()
23909 if (this.trigger) {
23910 this.trigger.removeAllListeners();
23911 this.trigger.remove();
23914 this.wrap.remove();
23916 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23919 checkStrength: function ()
23921 var pwd = this.inputEl().getValue();
23922 if (pwd == this._lastPwd) {
23927 if (this.ClientSideStrongPassword(pwd)) {
23929 } else if (this.ClientSideMediumPassword(pwd)) {
23931 } else if (this.ClientSideWeakPassword(pwd)) {
23937 Roo.log('strength1: ' + strength);
23939 //var pm = this.trigger.child('div/div/div').dom;
23940 var pm = this.trigger.child('div/div');
23941 pm.removeClass(this.meterClass);
23942 pm.addClass(this.meterClass[strength]);
23945 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23947 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23949 this._lastPwd = pwd;
23953 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23955 this._lastPwd = '';
23957 var pm = this.trigger.child('div/div');
23958 pm.removeClass(this.meterClass);
23959 pm.addClass('roo-password-meter-grey');
23962 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23965 this.inputEl().dom.type='password';
23968 validateValue: function (value)
23970 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23973 if (value.length == 0) {
23974 if (this.allowBlank) {
23975 this.clearInvalid();
23979 this.markInvalid(this.errors.PwdEmpty);
23980 this.errorMsg = this.errors.PwdEmpty;
23988 if (!value.match(/[\x21-\x7e]+/)) {
23989 this.markInvalid(this.errors.PwdBadChar);
23990 this.errorMsg = this.errors.PwdBadChar;
23993 if (value.length < 6) {
23994 this.markInvalid(this.errors.PwdShort);
23995 this.errorMsg = this.errors.PwdShort;
23998 if (value.length > 16) {
23999 this.markInvalid(this.errors.PwdLong);
24000 this.errorMsg = this.errors.PwdLong;
24004 if (this.ClientSideStrongPassword(value)) {
24006 } else if (this.ClientSideMediumPassword(value)) {
24008 } else if (this.ClientSideWeakPassword(value)) {
24015 if (strength < 2) {
24016 //this.markInvalid(this.errors.TooWeak);
24017 this.errorMsg = this.errors.TooWeak;
24022 console.log('strength2: ' + strength);
24024 //var pm = this.trigger.child('div/div/div').dom;
24026 var pm = this.trigger.child('div/div');
24027 pm.removeClass(this.meterClass);
24028 pm.addClass(this.meterClass[strength]);
24030 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24032 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24034 this.errorMsg = '';
24038 CharacterSetChecks: function (type)
24041 this.fResult = false;
24044 isctype: function (character, type)
24047 case this.kCapitalLetter:
24048 if (character >= 'A' && character <= 'Z') {
24053 case this.kSmallLetter:
24054 if (character >= 'a' && character <= 'z') {
24060 if (character >= '0' && character <= '9') {
24065 case this.kPunctuation:
24066 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24077 IsLongEnough: function (pwd, size)
24079 return !(pwd == null || isNaN(size) || pwd.length < size);
24082 SpansEnoughCharacterSets: function (word, nb)
24084 if (!this.IsLongEnough(word, nb))
24089 var characterSetChecks = new Array(
24090 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24091 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24094 for (var index = 0; index < word.length; ++index) {
24095 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24096 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24097 characterSetChecks[nCharSet].fResult = true;
24104 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24105 if (characterSetChecks[nCharSet].fResult) {
24110 if (nCharSets < nb) {
24116 ClientSideStrongPassword: function (pwd)
24118 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24121 ClientSideMediumPassword: function (pwd)
24123 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24126 ClientSideWeakPassword: function (pwd)
24128 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24131 })//<script type="text/javascript">
24134 * Based Ext JS Library 1.1.1
24135 * Copyright(c) 2006-2007, Ext JS, LLC.
24141 * @class Roo.HtmlEditorCore
24142 * @extends Roo.Component
24143 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24145 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24148 Roo.HtmlEditorCore = function(config){
24151 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24156 * @event initialize
24157 * Fires when the editor is fully initialized (including the iframe)
24158 * @param {Roo.HtmlEditorCore} this
24163 * Fires when the editor is first receives the focus. Any insertion must wait
24164 * until after this event.
24165 * @param {Roo.HtmlEditorCore} this
24169 * @event beforesync
24170 * Fires before the textarea is updated with content from the editor iframe. Return false
24171 * to cancel the sync.
24172 * @param {Roo.HtmlEditorCore} this
24173 * @param {String} html
24177 * @event beforepush
24178 * Fires before the iframe editor is updated with content from the textarea. Return false
24179 * to cancel the push.
24180 * @param {Roo.HtmlEditorCore} this
24181 * @param {String} html
24186 * Fires when the textarea is updated with content from the editor iframe.
24187 * @param {Roo.HtmlEditorCore} this
24188 * @param {String} html
24193 * Fires when the iframe editor is updated with content from the textarea.
24194 * @param {Roo.HtmlEditorCore} this
24195 * @param {String} html
24200 * @event editorevent
24201 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24202 * @param {Roo.HtmlEditorCore} this
24208 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24210 // defaults : white / black...
24211 this.applyBlacklists();
24218 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24222 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24228 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24233 * @cfg {Number} height (in pixels)
24237 * @cfg {Number} width (in pixels)
24242 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24245 stylesheets: false,
24250 // private properties
24251 validationEvent : false,
24253 initialized : false,
24255 sourceEditMode : false,
24256 onFocus : Roo.emptyFn,
24258 hideMode:'offsets',
24262 // blacklist + whitelisted elements..
24269 * Protected method that will not generally be called directly. It
24270 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24271 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24273 getDocMarkup : function(){
24277 // inherit styels from page...??
24278 if (this.stylesheets === false) {
24280 Roo.get(document.head).select('style').each(function(node) {
24281 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24284 Roo.get(document.head).select('link').each(function(node) {
24285 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24288 } else if (!this.stylesheets.length) {
24290 st = '<style type="text/css">' +
24291 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24294 for (var i in this.stylesheets) {
24295 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24300 st += '<style type="text/css">' +
24301 'IMG { cursor: pointer } ' +
24304 var cls = 'roo-htmleditor-body';
24306 if(this.bodyCls.length){
24307 cls += ' ' + this.bodyCls;
24310 return '<html><head>' + st +
24311 //<style type="text/css">' +
24312 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24314 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24318 onRender : function(ct, position)
24321 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24322 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24325 this.el.dom.style.border = '0 none';
24326 this.el.dom.setAttribute('tabIndex', -1);
24327 this.el.addClass('x-hidden hide');
24331 if(Roo.isIE){ // fix IE 1px bogus margin
24332 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24336 this.frameId = Roo.id();
24340 var iframe = this.owner.wrap.createChild({
24342 cls: 'form-control', // bootstrap..
24344 name: this.frameId,
24345 frameBorder : 'no',
24346 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24351 this.iframe = iframe.dom;
24353 this.assignDocWin();
24355 this.doc.designMode = 'on';
24358 this.doc.write(this.getDocMarkup());
24362 var task = { // must defer to wait for browser to be ready
24364 //console.log("run task?" + this.doc.readyState);
24365 this.assignDocWin();
24366 if(this.doc.body || this.doc.readyState == 'complete'){
24368 this.doc.designMode="on";
24372 Roo.TaskMgr.stop(task);
24373 this.initEditor.defer(10, this);
24380 Roo.TaskMgr.start(task);
24385 onResize : function(w, h)
24387 Roo.log('resize: ' +w + ',' + h );
24388 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24392 if(typeof w == 'number'){
24394 this.iframe.style.width = w + 'px';
24396 if(typeof h == 'number'){
24398 this.iframe.style.height = h + 'px';
24400 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24407 * Toggles the editor between standard and source edit mode.
24408 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24410 toggleSourceEdit : function(sourceEditMode){
24412 this.sourceEditMode = sourceEditMode === true;
24414 if(this.sourceEditMode){
24416 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24419 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24420 //this.iframe.className = '';
24423 //this.setSize(this.owner.wrap.getSize());
24424 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24431 * Protected method that will not generally be called directly. If you need/want
24432 * custom HTML cleanup, this is the method you should override.
24433 * @param {String} html The HTML to be cleaned
24434 * return {String} The cleaned HTML
24436 cleanHtml : function(html){
24437 html = String(html);
24438 if(html.length > 5){
24439 if(Roo.isSafari){ // strip safari nonsense
24440 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24443 if(html == ' '){
24450 * HTML Editor -> Textarea
24451 * Protected method that will not generally be called directly. Syncs the contents
24452 * of the editor iframe with the textarea.
24454 syncValue : function(){
24455 if(this.initialized){
24456 var bd = (this.doc.body || this.doc.documentElement);
24457 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24458 var html = bd.innerHTML;
24460 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24461 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24463 html = '<div style="'+m[0]+'">' + html + '</div>';
24466 html = this.cleanHtml(html);
24467 // fix up the special chars.. normaly like back quotes in word...
24468 // however we do not want to do this with chinese..
24469 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24471 var cc = match.charCodeAt();
24473 // Get the character value, handling surrogate pairs
24474 if (match.length == 2) {
24475 // It's a surrogate pair, calculate the Unicode code point
24476 var high = match.charCodeAt(0) - 0xD800;
24477 var low = match.charCodeAt(1) - 0xDC00;
24478 cc = (high * 0x400) + low + 0x10000;
24480 (cc >= 0x4E00 && cc < 0xA000 ) ||
24481 (cc >= 0x3400 && cc < 0x4E00 ) ||
24482 (cc >= 0xf900 && cc < 0xfb00 )
24487 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24488 return "&#" + cc + ";";
24495 if(this.owner.fireEvent('beforesync', this, html) !== false){
24496 this.el.dom.value = html;
24497 this.owner.fireEvent('sync', this, html);
24503 * Protected method that will not generally be called directly. Pushes the value of the textarea
24504 * into the iframe editor.
24506 pushValue : function(){
24507 if(this.initialized){
24508 var v = this.el.dom.value.trim();
24510 // if(v.length < 1){
24514 if(this.owner.fireEvent('beforepush', this, v) !== false){
24515 var d = (this.doc.body || this.doc.documentElement);
24517 this.cleanUpPaste();
24518 this.el.dom.value = d.innerHTML;
24519 this.owner.fireEvent('push', this, v);
24525 deferFocus : function(){
24526 this.focus.defer(10, this);
24530 focus : function(){
24531 if(this.win && !this.sourceEditMode){
24538 assignDocWin: function()
24540 var iframe = this.iframe;
24543 this.doc = iframe.contentWindow.document;
24544 this.win = iframe.contentWindow;
24546 // if (!Roo.get(this.frameId)) {
24549 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24550 // this.win = Roo.get(this.frameId).dom.contentWindow;
24552 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24556 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24557 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24562 initEditor : function(){
24563 //console.log("INIT EDITOR");
24564 this.assignDocWin();
24568 this.doc.designMode="on";
24570 this.doc.write(this.getDocMarkup());
24573 var dbody = (this.doc.body || this.doc.documentElement);
24574 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24575 // this copies styles from the containing element into thsi one..
24576 // not sure why we need all of this..
24577 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24579 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24580 //ss['background-attachment'] = 'fixed'; // w3c
24581 dbody.bgProperties = 'fixed'; // ie
24582 //Roo.DomHelper.applyStyles(dbody, ss);
24583 Roo.EventManager.on(this.doc, {
24584 //'mousedown': this.onEditorEvent,
24585 'mouseup': this.onEditorEvent,
24586 'dblclick': this.onEditorEvent,
24587 'click': this.onEditorEvent,
24588 'keyup': this.onEditorEvent,
24593 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24595 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24596 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24598 this.initialized = true;
24600 this.owner.fireEvent('initialize', this);
24605 onDestroy : function(){
24611 //for (var i =0; i < this.toolbars.length;i++) {
24612 // // fixme - ask toolbars for heights?
24613 // this.toolbars[i].onDestroy();
24616 //this.wrap.dom.innerHTML = '';
24617 //this.wrap.remove();
24622 onFirstFocus : function(){
24624 this.assignDocWin();
24627 this.activated = true;
24630 if(Roo.isGecko){ // prevent silly gecko errors
24632 var s = this.win.getSelection();
24633 if(!s.focusNode || s.focusNode.nodeType != 3){
24634 var r = s.getRangeAt(0);
24635 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24640 this.execCmd('useCSS', true);
24641 this.execCmd('styleWithCSS', false);
24644 this.owner.fireEvent('activate', this);
24648 adjustFont: function(btn){
24649 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24650 //if(Roo.isSafari){ // safari
24653 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24654 if(Roo.isSafari){ // safari
24655 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24656 v = (v < 10) ? 10 : v;
24657 v = (v > 48) ? 48 : v;
24658 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24663 v = Math.max(1, v+adjust);
24665 this.execCmd('FontSize', v );
24668 onEditorEvent : function(e)
24670 this.owner.fireEvent('editorevent', this, e);
24671 // this.updateToolbar();
24672 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24675 insertTag : function(tg)
24677 // could be a bit smarter... -> wrap the current selected tRoo..
24678 if (tg.toLowerCase() == 'span' ||
24679 tg.toLowerCase() == 'code' ||
24680 tg.toLowerCase() == 'sup' ||
24681 tg.toLowerCase() == 'sub'
24684 range = this.createRange(this.getSelection());
24685 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24686 wrappingNode.appendChild(range.extractContents());
24687 range.insertNode(wrappingNode);
24694 this.execCmd("formatblock", tg);
24698 insertText : function(txt)
24702 var range = this.createRange();
24703 range.deleteContents();
24704 //alert(Sender.getAttribute('label'));
24706 range.insertNode(this.doc.createTextNode(txt));
24712 * Executes a Midas editor command on the editor document and performs necessary focus and
24713 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24714 * @param {String} cmd The Midas command
24715 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24717 relayCmd : function(cmd, value){
24719 this.execCmd(cmd, value);
24720 this.owner.fireEvent('editorevent', this);
24721 //this.updateToolbar();
24722 this.owner.deferFocus();
24726 * Executes a Midas editor command directly on the editor document.
24727 * For visual commands, you should use {@link #relayCmd} instead.
24728 * <b>This should only be called after the editor is initialized.</b>
24729 * @param {String} cmd The Midas command
24730 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24732 execCmd : function(cmd, value){
24733 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24740 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24742 * @param {String} text | dom node..
24744 insertAtCursor : function(text)
24747 if(!this.activated){
24753 var r = this.doc.selection.createRange();
24764 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24768 // from jquery ui (MIT licenced)
24770 var win = this.win;
24772 if (win.getSelection && win.getSelection().getRangeAt) {
24773 range = win.getSelection().getRangeAt(0);
24774 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24775 range.insertNode(node);
24776 } else if (win.document.selection && win.document.selection.createRange) {
24777 // no firefox support
24778 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24779 win.document.selection.createRange().pasteHTML(txt);
24781 // no firefox support
24782 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24783 this.execCmd('InsertHTML', txt);
24792 mozKeyPress : function(e){
24794 var c = e.getCharCode(), cmd;
24797 c = String.fromCharCode(c).toLowerCase();
24811 this.cleanUpPaste.defer(100, this);
24819 e.preventDefault();
24827 fixKeys : function(){ // load time branching for fastest keydown performance
24829 return function(e){
24830 var k = e.getKey(), r;
24833 r = this.doc.selection.createRange();
24836 r.pasteHTML('    ');
24843 r = this.doc.selection.createRange();
24845 var target = r.parentElement();
24846 if(!target || target.tagName.toLowerCase() != 'li'){
24848 r.pasteHTML('<br />');
24854 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24855 this.cleanUpPaste.defer(100, this);
24861 }else if(Roo.isOpera){
24862 return function(e){
24863 var k = e.getKey();
24867 this.execCmd('InsertHTML','    ');
24870 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24871 this.cleanUpPaste.defer(100, this);
24876 }else if(Roo.isSafari){
24877 return function(e){
24878 var k = e.getKey();
24882 this.execCmd('InsertText','\t');
24886 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24887 this.cleanUpPaste.defer(100, this);
24895 getAllAncestors: function()
24897 var p = this.getSelectedNode();
24900 a.push(p); // push blank onto stack..
24901 p = this.getParentElement();
24905 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24909 a.push(this.doc.body);
24913 lastSelNode : false,
24916 getSelection : function()
24918 this.assignDocWin();
24919 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24922 getSelectedNode: function()
24924 // this may only work on Gecko!!!
24926 // should we cache this!!!!
24931 var range = this.createRange(this.getSelection()).cloneRange();
24934 var parent = range.parentElement();
24936 var testRange = range.duplicate();
24937 testRange.moveToElementText(parent);
24938 if (testRange.inRange(range)) {
24941 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24944 parent = parent.parentElement;
24949 // is ancestor a text element.
24950 var ac = range.commonAncestorContainer;
24951 if (ac.nodeType == 3) {
24952 ac = ac.parentNode;
24955 var ar = ac.childNodes;
24958 var other_nodes = [];
24959 var has_other_nodes = false;
24960 for (var i=0;i<ar.length;i++) {
24961 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24964 // fullly contained node.
24966 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24971 // probably selected..
24972 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24973 other_nodes.push(ar[i]);
24977 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24982 has_other_nodes = true;
24984 if (!nodes.length && other_nodes.length) {
24985 nodes= other_nodes;
24987 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24993 createRange: function(sel)
24995 // this has strange effects when using with
24996 // top toolbar - not sure if it's a great idea.
24997 //this.editor.contentWindow.focus();
24998 if (typeof sel != "undefined") {
25000 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25002 return this.doc.createRange();
25005 return this.doc.createRange();
25008 getParentElement: function()
25011 this.assignDocWin();
25012 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25014 var range = this.createRange(sel);
25017 var p = range.commonAncestorContainer;
25018 while (p.nodeType == 3) { // text node
25029 * Range intersection.. the hard stuff...
25033 * [ -- selected range --- ]
25037 * if end is before start or hits it. fail.
25038 * if start is after end or hits it fail.
25040 * if either hits (but other is outside. - then it's not
25046 // @see http://www.thismuchiknow.co.uk/?p=64.
25047 rangeIntersectsNode : function(range, node)
25049 var nodeRange = node.ownerDocument.createRange();
25051 nodeRange.selectNode(node);
25053 nodeRange.selectNodeContents(node);
25056 var rangeStartRange = range.cloneRange();
25057 rangeStartRange.collapse(true);
25059 var rangeEndRange = range.cloneRange();
25060 rangeEndRange.collapse(false);
25062 var nodeStartRange = nodeRange.cloneRange();
25063 nodeStartRange.collapse(true);
25065 var nodeEndRange = nodeRange.cloneRange();
25066 nodeEndRange.collapse(false);
25068 return rangeStartRange.compareBoundaryPoints(
25069 Range.START_TO_START, nodeEndRange) == -1 &&
25070 rangeEndRange.compareBoundaryPoints(
25071 Range.START_TO_START, nodeStartRange) == 1;
25075 rangeCompareNode : function(range, node)
25077 var nodeRange = node.ownerDocument.createRange();
25079 nodeRange.selectNode(node);
25081 nodeRange.selectNodeContents(node);
25085 range.collapse(true);
25087 nodeRange.collapse(true);
25089 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25090 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25092 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25094 var nodeIsBefore = ss == 1;
25095 var nodeIsAfter = ee == -1;
25097 if (nodeIsBefore && nodeIsAfter) {
25100 if (!nodeIsBefore && nodeIsAfter) {
25101 return 1; //right trailed.
25104 if (nodeIsBefore && !nodeIsAfter) {
25105 return 2; // left trailed.
25111 // private? - in a new class?
25112 cleanUpPaste : function()
25114 // cleans up the whole document..
25115 Roo.log('cleanuppaste');
25117 this.cleanUpChildren(this.doc.body);
25118 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25119 if (clean != this.doc.body.innerHTML) {
25120 this.doc.body.innerHTML = clean;
25125 cleanWordChars : function(input) {// change the chars to hex code
25126 var he = Roo.HtmlEditorCore;
25128 var output = input;
25129 Roo.each(he.swapCodes, function(sw) {
25130 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25132 output = output.replace(swapper, sw[1]);
25139 cleanUpChildren : function (n)
25141 if (!n.childNodes.length) {
25144 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25145 this.cleanUpChild(n.childNodes[i]);
25152 cleanUpChild : function (node)
25155 //console.log(node);
25156 if (node.nodeName == "#text") {
25157 // clean up silly Windows -- stuff?
25160 if (node.nodeName == "#comment") {
25161 node.parentNode.removeChild(node);
25162 // clean up silly Windows -- stuff?
25165 var lcname = node.tagName.toLowerCase();
25166 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25167 // whitelist of tags..
25169 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25171 node.parentNode.removeChild(node);
25176 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25178 // spans with no attributes - just remove them..
25179 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25180 remove_keep_children = true;
25183 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25184 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25186 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25187 // remove_keep_children = true;
25190 if (remove_keep_children) {
25191 this.cleanUpChildren(node);
25192 // inserts everything just before this node...
25193 while (node.childNodes.length) {
25194 var cn = node.childNodes[0];
25195 node.removeChild(cn);
25196 node.parentNode.insertBefore(cn, node);
25198 node.parentNode.removeChild(node);
25202 if (!node.attributes || !node.attributes.length) {
25207 this.cleanUpChildren(node);
25211 function cleanAttr(n,v)
25214 if (v.match(/^\./) || v.match(/^\//)) {
25217 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25220 if (v.match(/^#/)) {
25223 if (v.match(/^\{/)) { // allow template editing.
25226 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25227 node.removeAttribute(n);
25231 var cwhite = this.cwhite;
25232 var cblack = this.cblack;
25234 function cleanStyle(n,v)
25236 if (v.match(/expression/)) { //XSS?? should we even bother..
25237 node.removeAttribute(n);
25241 var parts = v.split(/;/);
25244 Roo.each(parts, function(p) {
25245 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25249 var l = p.split(':').shift().replace(/\s+/g,'');
25250 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25252 if ( cwhite.length && cblack.indexOf(l) > -1) {
25253 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25254 //node.removeAttribute(n);
25258 // only allow 'c whitelisted system attributes'
25259 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25260 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25261 //node.removeAttribute(n);
25271 if (clean.length) {
25272 node.setAttribute(n, clean.join(';'));
25274 node.removeAttribute(n);
25280 for (var i = node.attributes.length-1; i > -1 ; i--) {
25281 var a = node.attributes[i];
25284 if (a.name.toLowerCase().substr(0,2)=='on') {
25285 node.removeAttribute(a.name);
25288 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25289 node.removeAttribute(a.name);
25292 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25293 cleanAttr(a.name,a.value); // fixme..
25296 if (a.name == 'style') {
25297 cleanStyle(a.name,a.value);
25300 /// clean up MS crap..
25301 // tecnically this should be a list of valid class'es..
25304 if (a.name == 'class') {
25305 if (a.value.match(/^Mso/)) {
25306 node.removeAttribute('class');
25309 if (a.value.match(/^body$/)) {
25310 node.removeAttribute('class');
25321 this.cleanUpChildren(node);
25327 * Clean up MS wordisms...
25329 cleanWord : function(node)
25332 this.cleanWord(this.doc.body);
25337 node.nodeName == 'SPAN' &&
25338 !node.hasAttributes() &&
25339 node.childNodes.length == 1 &&
25340 node.firstChild.nodeName == "#text"
25342 var textNode = node.firstChild;
25343 node.removeChild(textNode);
25344 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25345 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25347 node.parentNode.insertBefore(textNode, node);
25348 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25349 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25351 node.parentNode.removeChild(node);
25354 if (node.nodeName == "#text") {
25355 // clean up silly Windows -- stuff?
25358 if (node.nodeName == "#comment") {
25359 node.parentNode.removeChild(node);
25360 // clean up silly Windows -- stuff?
25364 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25365 node.parentNode.removeChild(node);
25368 //Roo.log(node.tagName);
25369 // remove - but keep children..
25370 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25371 //Roo.log('-- removed');
25372 while (node.childNodes.length) {
25373 var cn = node.childNodes[0];
25374 node.removeChild(cn);
25375 node.parentNode.insertBefore(cn, node);
25376 // move node to parent - and clean it..
25377 this.cleanWord(cn);
25379 node.parentNode.removeChild(node);
25380 /// no need to iterate chidlren = it's got none..
25381 //this.iterateChildren(node, this.cleanWord);
25385 if (node.className.length) {
25387 var cn = node.className.split(/\W+/);
25389 Roo.each(cn, function(cls) {
25390 if (cls.match(/Mso[a-zA-Z]+/)) {
25395 node.className = cna.length ? cna.join(' ') : '';
25397 node.removeAttribute("class");
25401 if (node.hasAttribute("lang")) {
25402 node.removeAttribute("lang");
25405 if (node.hasAttribute("style")) {
25407 var styles = node.getAttribute("style").split(";");
25409 Roo.each(styles, function(s) {
25410 if (!s.match(/:/)) {
25413 var kv = s.split(":");
25414 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25417 // what ever is left... we allow.
25420 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25421 if (!nstyle.length) {
25422 node.removeAttribute('style');
25425 this.iterateChildren(node, this.cleanWord);
25431 * iterateChildren of a Node, calling fn each time, using this as the scole..
25432 * @param {DomNode} node node to iterate children of.
25433 * @param {Function} fn method of this class to call on each item.
25435 iterateChildren : function(node, fn)
25437 if (!node.childNodes.length) {
25440 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25441 fn.call(this, node.childNodes[i])
25447 * cleanTableWidths.
25449 * Quite often pasting from word etc.. results in tables with column and widths.
25450 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25453 cleanTableWidths : function(node)
25458 this.cleanTableWidths(this.doc.body);
25463 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25466 Roo.log(node.tagName);
25467 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25468 this.iterateChildren(node, this.cleanTableWidths);
25471 if (node.hasAttribute('width')) {
25472 node.removeAttribute('width');
25476 if (node.hasAttribute("style")) {
25479 var styles = node.getAttribute("style").split(";");
25481 Roo.each(styles, function(s) {
25482 if (!s.match(/:/)) {
25485 var kv = s.split(":");
25486 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25489 // what ever is left... we allow.
25492 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25493 if (!nstyle.length) {
25494 node.removeAttribute('style');
25498 this.iterateChildren(node, this.cleanTableWidths);
25506 domToHTML : function(currentElement, depth, nopadtext) {
25508 depth = depth || 0;
25509 nopadtext = nopadtext || false;
25511 if (!currentElement) {
25512 return this.domToHTML(this.doc.body);
25515 //Roo.log(currentElement);
25517 var allText = false;
25518 var nodeName = currentElement.nodeName;
25519 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25521 if (nodeName == '#text') {
25523 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25528 if (nodeName != 'BODY') {
25531 // Prints the node tagName, such as <A>, <IMG>, etc
25534 for(i = 0; i < currentElement.attributes.length;i++) {
25536 var aname = currentElement.attributes.item(i).name;
25537 if (!currentElement.attributes.item(i).value.length) {
25540 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25543 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25552 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25555 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25560 // Traverse the tree
25562 var currentElementChild = currentElement.childNodes.item(i);
25563 var allText = true;
25564 var innerHTML = '';
25566 while (currentElementChild) {
25567 // Formatting code (indent the tree so it looks nice on the screen)
25568 var nopad = nopadtext;
25569 if (lastnode == 'SPAN') {
25573 if (currentElementChild.nodeName == '#text') {
25574 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25575 toadd = nopadtext ? toadd : toadd.trim();
25576 if (!nopad && toadd.length > 80) {
25577 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25579 innerHTML += toadd;
25582 currentElementChild = currentElement.childNodes.item(i);
25588 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25590 // Recursively traverse the tree structure of the child node
25591 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25592 lastnode = currentElementChild.nodeName;
25594 currentElementChild=currentElement.childNodes.item(i);
25600 // The remaining code is mostly for formatting the tree
25601 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25606 ret+= "</"+tagName+">";
25612 applyBlacklists : function()
25614 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25615 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25619 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25620 if (b.indexOf(tag) > -1) {
25623 this.white.push(tag);
25627 Roo.each(w, function(tag) {
25628 if (b.indexOf(tag) > -1) {
25631 if (this.white.indexOf(tag) > -1) {
25634 this.white.push(tag);
25639 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25640 if (w.indexOf(tag) > -1) {
25643 this.black.push(tag);
25647 Roo.each(b, function(tag) {
25648 if (w.indexOf(tag) > -1) {
25651 if (this.black.indexOf(tag) > -1) {
25654 this.black.push(tag);
25659 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25660 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25664 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25665 if (b.indexOf(tag) > -1) {
25668 this.cwhite.push(tag);
25672 Roo.each(w, function(tag) {
25673 if (b.indexOf(tag) > -1) {
25676 if (this.cwhite.indexOf(tag) > -1) {
25679 this.cwhite.push(tag);
25684 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25685 if (w.indexOf(tag) > -1) {
25688 this.cblack.push(tag);
25692 Roo.each(b, function(tag) {
25693 if (w.indexOf(tag) > -1) {
25696 if (this.cblack.indexOf(tag) > -1) {
25699 this.cblack.push(tag);
25704 setStylesheets : function(stylesheets)
25706 if(typeof(stylesheets) == 'string'){
25707 Roo.get(this.iframe.contentDocument.head).createChild({
25709 rel : 'stylesheet',
25718 Roo.each(stylesheets, function(s) {
25723 Roo.get(_this.iframe.contentDocument.head).createChild({
25725 rel : 'stylesheet',
25734 removeStylesheets : function()
25738 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25743 setStyle : function(style)
25745 Roo.get(this.iframe.contentDocument.head).createChild({
25754 // hide stuff that is not compatible
25768 * @event specialkey
25772 * @cfg {String} fieldClass @hide
25775 * @cfg {String} focusClass @hide
25778 * @cfg {String} autoCreate @hide
25781 * @cfg {String} inputType @hide
25784 * @cfg {String} invalidClass @hide
25787 * @cfg {String} invalidText @hide
25790 * @cfg {String} msgFx @hide
25793 * @cfg {String} validateOnBlur @hide
25797 Roo.HtmlEditorCore.white = [
25798 'area', 'br', 'img', 'input', 'hr', 'wbr',
25800 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25801 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25802 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25803 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25804 'table', 'ul', 'xmp',
25806 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25809 'dir', 'menu', 'ol', 'ul', 'dl',
25815 Roo.HtmlEditorCore.black = [
25816 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25818 'base', 'basefont', 'bgsound', 'blink', 'body',
25819 'frame', 'frameset', 'head', 'html', 'ilayer',
25820 'iframe', 'layer', 'link', 'meta', 'object',
25821 'script', 'style' ,'title', 'xml' // clean later..
25823 Roo.HtmlEditorCore.clean = [
25824 'script', 'style', 'title', 'xml'
25826 Roo.HtmlEditorCore.remove = [
25831 Roo.HtmlEditorCore.ablack = [
25835 Roo.HtmlEditorCore.aclean = [
25836 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25840 Roo.HtmlEditorCore.pwhite= [
25841 'http', 'https', 'mailto'
25844 // white listed style attributes.
25845 Roo.HtmlEditorCore.cwhite= [
25846 // 'text-align', /// default is to allow most things..
25852 // black listed style attributes.
25853 Roo.HtmlEditorCore.cblack= [
25854 // 'font-size' -- this can be set by the project
25858 Roo.HtmlEditorCore.swapCodes =[
25859 [ 8211, "–" ],
25860 [ 8212, "—" ],
25877 * @class Roo.bootstrap.HtmlEditor
25878 * @extends Roo.bootstrap.TextArea
25879 * Bootstrap HtmlEditor class
25882 * Create a new HtmlEditor
25883 * @param {Object} config The config object
25886 Roo.bootstrap.HtmlEditor = function(config){
25887 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25888 if (!this.toolbars) {
25889 this.toolbars = [];
25892 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25895 * @event initialize
25896 * Fires when the editor is fully initialized (including the iframe)
25897 * @param {HtmlEditor} this
25902 * Fires when the editor is first receives the focus. Any insertion must wait
25903 * until after this event.
25904 * @param {HtmlEditor} this
25908 * @event beforesync
25909 * Fires before the textarea is updated with content from the editor iframe. Return false
25910 * to cancel the sync.
25911 * @param {HtmlEditor} this
25912 * @param {String} html
25916 * @event beforepush
25917 * Fires before the iframe editor is updated with content from the textarea. Return false
25918 * to cancel the push.
25919 * @param {HtmlEditor} this
25920 * @param {String} html
25925 * Fires when the textarea is updated with content from the editor iframe.
25926 * @param {HtmlEditor} this
25927 * @param {String} html
25932 * Fires when the iframe editor is updated with content from the textarea.
25933 * @param {HtmlEditor} this
25934 * @param {String} html
25938 * @event editmodechange
25939 * Fires when the editor switches edit modes
25940 * @param {HtmlEditor} this
25941 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25943 editmodechange: true,
25945 * @event editorevent
25946 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25947 * @param {HtmlEditor} this
25951 * @event firstfocus
25952 * Fires when on first focus - needed by toolbars..
25953 * @param {HtmlEditor} this
25958 * Auto save the htmlEditor value as a file into Events
25959 * @param {HtmlEditor} this
25963 * @event savedpreview
25964 * preview the saved version of htmlEditor
25965 * @param {HtmlEditor} this
25972 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25976 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25981 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25986 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25991 * @cfg {Number} height (in pixels)
25995 * @cfg {Number} width (in pixels)
26000 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26003 stylesheets: false,
26008 // private properties
26009 validationEvent : false,
26011 initialized : false,
26014 onFocus : Roo.emptyFn,
26016 hideMode:'offsets',
26018 tbContainer : false,
26022 toolbarContainer :function() {
26023 return this.wrap.select('.x-html-editor-tb',true).first();
26027 * Protected method that will not generally be called directly. It
26028 * is called when the editor creates its toolbar. Override this method if you need to
26029 * add custom toolbar buttons.
26030 * @param {HtmlEditor} editor
26032 createToolbar : function(){
26033 Roo.log('renewing');
26034 Roo.log("create toolbars");
26036 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26037 this.toolbars[0].render(this.toolbarContainer());
26041 // if (!editor.toolbars || !editor.toolbars.length) {
26042 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26045 // for (var i =0 ; i < editor.toolbars.length;i++) {
26046 // editor.toolbars[i] = Roo.factory(
26047 // typeof(editor.toolbars[i]) == 'string' ?
26048 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26049 // Roo.bootstrap.HtmlEditor);
26050 // editor.toolbars[i].init(editor);
26056 onRender : function(ct, position)
26058 // Roo.log("Call onRender: " + this.xtype);
26060 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26062 this.wrap = this.inputEl().wrap({
26063 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26066 this.editorcore.onRender(ct, position);
26068 if (this.resizable) {
26069 this.resizeEl = new Roo.Resizable(this.wrap, {
26073 minHeight : this.height,
26074 height: this.height,
26075 handles : this.resizable,
26078 resize : function(r, w, h) {
26079 _t.onResize(w,h); // -something
26085 this.createToolbar(this);
26088 if(!this.width && this.resizable){
26089 this.setSize(this.wrap.getSize());
26091 if (this.resizeEl) {
26092 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26093 // should trigger onReize..
26099 onResize : function(w, h)
26101 Roo.log('resize: ' +w + ',' + h );
26102 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26106 if(this.inputEl() ){
26107 if(typeof w == 'number'){
26108 var aw = w - this.wrap.getFrameWidth('lr');
26109 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26112 if(typeof h == 'number'){
26113 var tbh = -11; // fixme it needs to tool bar size!
26114 for (var i =0; i < this.toolbars.length;i++) {
26115 // fixme - ask toolbars for heights?
26116 tbh += this.toolbars[i].el.getHeight();
26117 //if (this.toolbars[i].footer) {
26118 // tbh += this.toolbars[i].footer.el.getHeight();
26126 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26127 ah -= 5; // knock a few pixes off for look..
26128 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26132 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26133 this.editorcore.onResize(ew,eh);
26138 * Toggles the editor between standard and source edit mode.
26139 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26141 toggleSourceEdit : function(sourceEditMode)
26143 this.editorcore.toggleSourceEdit(sourceEditMode);
26145 if(this.editorcore.sourceEditMode){
26146 Roo.log('editor - showing textarea');
26149 // Roo.log(this.syncValue());
26151 this.inputEl().removeClass(['hide', 'x-hidden']);
26152 this.inputEl().dom.removeAttribute('tabIndex');
26153 this.inputEl().focus();
26155 Roo.log('editor - hiding textarea');
26157 // Roo.log(this.pushValue());
26160 this.inputEl().addClass(['hide', 'x-hidden']);
26161 this.inputEl().dom.setAttribute('tabIndex', -1);
26162 //this.deferFocus();
26165 if(this.resizable){
26166 this.setSize(this.wrap.getSize());
26169 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26172 // private (for BoxComponent)
26173 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26175 // private (for BoxComponent)
26176 getResizeEl : function(){
26180 // private (for BoxComponent)
26181 getPositionEl : function(){
26186 initEvents : function(){
26187 this.originalValue = this.getValue();
26191 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26194 // markInvalid : Roo.emptyFn,
26196 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26199 // clearInvalid : Roo.emptyFn,
26201 setValue : function(v){
26202 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26203 this.editorcore.pushValue();
26208 deferFocus : function(){
26209 this.focus.defer(10, this);
26213 focus : function(){
26214 this.editorcore.focus();
26220 onDestroy : function(){
26226 for (var i =0; i < this.toolbars.length;i++) {
26227 // fixme - ask toolbars for heights?
26228 this.toolbars[i].onDestroy();
26231 this.wrap.dom.innerHTML = '';
26232 this.wrap.remove();
26237 onFirstFocus : function(){
26238 //Roo.log("onFirstFocus");
26239 this.editorcore.onFirstFocus();
26240 for (var i =0; i < this.toolbars.length;i++) {
26241 this.toolbars[i].onFirstFocus();
26247 syncValue : function()
26249 this.editorcore.syncValue();
26252 pushValue : function()
26254 this.editorcore.pushValue();
26258 // hide stuff that is not compatible
26272 * @event specialkey
26276 * @cfg {String} fieldClass @hide
26279 * @cfg {String} focusClass @hide
26282 * @cfg {String} autoCreate @hide
26285 * @cfg {String} inputType @hide
26289 * @cfg {String} invalidText @hide
26292 * @cfg {String} msgFx @hide
26295 * @cfg {String} validateOnBlur @hide
26304 Roo.namespace('Roo.bootstrap.htmleditor');
26306 * @class Roo.bootstrap.HtmlEditorToolbar1
26312 new Roo.bootstrap.HtmlEditor({
26315 new Roo.bootstrap.HtmlEditorToolbar1({
26316 disable : { fonts: 1 , format: 1, ..., ... , ...],
26322 * @cfg {Object} disable List of elements to disable..
26323 * @cfg {Array} btns List of additional buttons.
26327 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26330 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26333 Roo.apply(this, config);
26335 // default disabled, based on 'good practice'..
26336 this.disable = this.disable || {};
26337 Roo.applyIf(this.disable, {
26340 specialElements : true
26342 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26344 this.editor = config.editor;
26345 this.editorcore = config.editor.editorcore;
26347 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26349 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26350 // dont call parent... till later.
26352 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26357 editorcore : false,
26362 "h1","h2","h3","h4","h5","h6",
26364 "abbr", "acronym", "address", "cite", "samp", "var",
26368 onRender : function(ct, position)
26370 // Roo.log("Call onRender: " + this.xtype);
26372 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26374 this.el.dom.style.marginBottom = '0';
26376 var editorcore = this.editorcore;
26377 var editor= this.editor;
26380 var btn = function(id,cmd , toggle, handler, html){
26382 var event = toggle ? 'toggle' : 'click';
26387 xns: Roo.bootstrap,
26391 enableToggle:toggle !== false,
26393 pressed : toggle ? false : null,
26396 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26397 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26403 // var cb_box = function...
26408 xns: Roo.bootstrap,
26413 xns: Roo.bootstrap,
26417 Roo.each(this.formats, function(f) {
26418 style.menu.items.push({
26420 xns: Roo.bootstrap,
26421 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26426 editorcore.insertTag(this.tagname);
26433 children.push(style);
26435 btn('bold',false,true);
26436 btn('italic',false,true);
26437 btn('align-left', 'justifyleft',true);
26438 btn('align-center', 'justifycenter',true);
26439 btn('align-right' , 'justifyright',true);
26440 btn('link', false, false, function(btn) {
26441 //Roo.log("create link?");
26442 var url = prompt(this.createLinkText, this.defaultLinkValue);
26443 if(url && url != 'http:/'+'/'){
26444 this.editorcore.relayCmd('createlink', url);
26447 btn('list','insertunorderedlist',true);
26448 btn('pencil', false,true, function(btn){
26450 this.toggleSourceEdit(btn.pressed);
26453 if (this.editor.btns.length > 0) {
26454 for (var i = 0; i<this.editor.btns.length; i++) {
26455 children.push(this.editor.btns[i]);
26463 xns: Roo.bootstrap,
26468 xns: Roo.bootstrap,
26473 cog.menu.items.push({
26475 xns: Roo.bootstrap,
26476 html : Clean styles,
26481 editorcore.insertTag(this.tagname);
26490 this.xtype = 'NavSimplebar';
26492 for(var i=0;i< children.length;i++) {
26494 this.buttons.add(this.addxtypeChild(children[i]));
26498 editor.on('editorevent', this.updateToolbar, this);
26500 onBtnClick : function(id)
26502 this.editorcore.relayCmd(id);
26503 this.editorcore.focus();
26507 * Protected method that will not generally be called directly. It triggers
26508 * a toolbar update by reading the markup state of the current selection in the editor.
26510 updateToolbar: function(){
26512 if(!this.editorcore.activated){
26513 this.editor.onFirstFocus(); // is this neeed?
26517 var btns = this.buttons;
26518 var doc = this.editorcore.doc;
26519 btns.get('bold').setActive(doc.queryCommandState('bold'));
26520 btns.get('italic').setActive(doc.queryCommandState('italic'));
26521 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26523 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26524 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26525 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26527 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26528 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26531 var ans = this.editorcore.getAllAncestors();
26532 if (this.formatCombo) {
26535 var store = this.formatCombo.store;
26536 this.formatCombo.setValue("");
26537 for (var i =0; i < ans.length;i++) {
26538 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26540 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26548 // hides menus... - so this cant be on a menu...
26549 Roo.bootstrap.MenuMgr.hideAll();
26551 Roo.bootstrap.MenuMgr.hideAll();
26552 //this.editorsyncValue();
26554 onFirstFocus: function() {
26555 this.buttons.each(function(item){
26559 toggleSourceEdit : function(sourceEditMode){
26562 if(sourceEditMode){
26563 Roo.log("disabling buttons");
26564 this.buttons.each( function(item){
26565 if(item.cmd != 'pencil'){
26571 Roo.log("enabling buttons");
26572 if(this.editorcore.initialized){
26573 this.buttons.each( function(item){
26579 Roo.log("calling toggole on editor");
26580 // tell the editor that it's been pressed..
26581 this.editor.toggleSourceEdit(sourceEditMode);
26595 * @class Roo.bootstrap.Markdown
26596 * @extends Roo.bootstrap.TextArea
26597 * Bootstrap Showdown editable area
26598 * @cfg {string} content
26601 * Create a new Showdown
26604 Roo.bootstrap.Markdown = function(config){
26605 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26609 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26613 initEvents : function()
26616 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26617 this.markdownEl = this.el.createChild({
26618 cls : 'roo-markdown-area'
26620 this.inputEl().addClass('d-none');
26621 if (this.getValue() == '') {
26622 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26625 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26627 this.markdownEl.on('click', this.toggleTextEdit, this);
26628 this.on('blur', this.toggleTextEdit, this);
26629 this.on('specialkey', this.resizeTextArea, this);
26632 toggleTextEdit : function()
26634 var sh = this.markdownEl.getHeight();
26635 this.inputEl().addClass('d-none');
26636 this.markdownEl.addClass('d-none');
26637 if (!this.editing) {
26639 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26640 this.inputEl().removeClass('d-none');
26641 this.inputEl().focus();
26642 this.editing = true;
26645 // show showdown...
26646 this.updateMarkdown();
26647 this.markdownEl.removeClass('d-none');
26648 this.editing = false;
26651 updateMarkdown : function()
26653 if (this.getValue() == '') {
26654 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26658 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26661 resizeTextArea: function () {
26664 Roo.log([sh, this.getValue().split("\n").length * 30]);
26665 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26667 setValue : function(val)
26669 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26670 if (!this.editing) {
26671 this.updateMarkdown();
26677 if (!this.editing) {
26678 this.toggleTextEdit();
26686 * @class Roo.bootstrap.Table.AbstractSelectionModel
26687 * @extends Roo.util.Observable
26688 * Abstract base class for grid SelectionModels. It provides the interface that should be
26689 * implemented by descendant classes. This class should not be directly instantiated.
26692 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26693 this.locked = false;
26694 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26698 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26699 /** @ignore Called by the grid automatically. Do not call directly. */
26700 init : function(grid){
26706 * Locks the selections.
26709 this.locked = true;
26713 * Unlocks the selections.
26715 unlock : function(){
26716 this.locked = false;
26720 * Returns true if the selections are locked.
26721 * @return {Boolean}
26723 isLocked : function(){
26724 return this.locked;
26728 initEvents : function ()
26734 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26735 * @class Roo.bootstrap.Table.RowSelectionModel
26736 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26737 * It supports multiple selections and keyboard selection/navigation.
26739 * @param {Object} config
26742 Roo.bootstrap.Table.RowSelectionModel = function(config){
26743 Roo.apply(this, config);
26744 this.selections = new Roo.util.MixedCollection(false, function(o){
26749 this.lastActive = false;
26753 * @event selectionchange
26754 * Fires when the selection changes
26755 * @param {SelectionModel} this
26757 "selectionchange" : true,
26759 * @event afterselectionchange
26760 * Fires after the selection changes (eg. by key press or clicking)
26761 * @param {SelectionModel} this
26763 "afterselectionchange" : true,
26765 * @event beforerowselect
26766 * Fires when a row is selected being selected, return false to cancel.
26767 * @param {SelectionModel} this
26768 * @param {Number} rowIndex The selected index
26769 * @param {Boolean} keepExisting False if other selections will be cleared
26771 "beforerowselect" : true,
26774 * Fires when a row is selected.
26775 * @param {SelectionModel} this
26776 * @param {Number} rowIndex The selected index
26777 * @param {Roo.data.Record} r The record
26779 "rowselect" : true,
26781 * @event rowdeselect
26782 * Fires when a row is deselected.
26783 * @param {SelectionModel} this
26784 * @param {Number} rowIndex The selected index
26786 "rowdeselect" : true
26788 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26789 this.locked = false;
26792 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26794 * @cfg {Boolean} singleSelect
26795 * True to allow selection of only one row at a time (defaults to false)
26797 singleSelect : false,
26800 initEvents : function()
26803 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26804 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26805 //}else{ // allow click to work like normal
26806 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26808 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26809 this.grid.on("rowclick", this.handleMouseDown, this);
26811 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26812 "up" : function(e){
26814 this.selectPrevious(e.shiftKey);
26815 }else if(this.last !== false && this.lastActive !== false){
26816 var last = this.last;
26817 this.selectRange(this.last, this.lastActive-1);
26818 this.grid.getView().focusRow(this.lastActive);
26819 if(last !== false){
26823 this.selectFirstRow();
26825 this.fireEvent("afterselectionchange", this);
26827 "down" : function(e){
26829 this.selectNext(e.shiftKey);
26830 }else if(this.last !== false && this.lastActive !== false){
26831 var last = this.last;
26832 this.selectRange(this.last, this.lastActive+1);
26833 this.grid.getView().focusRow(this.lastActive);
26834 if(last !== false){
26838 this.selectFirstRow();
26840 this.fireEvent("afterselectionchange", this);
26844 this.grid.store.on('load', function(){
26845 this.selections.clear();
26848 var view = this.grid.view;
26849 view.on("refresh", this.onRefresh, this);
26850 view.on("rowupdated", this.onRowUpdated, this);
26851 view.on("rowremoved", this.onRemove, this);
26856 onRefresh : function()
26858 var ds = this.grid.store, i, v = this.grid.view;
26859 var s = this.selections;
26860 s.each(function(r){
26861 if((i = ds.indexOfId(r.id)) != -1){
26870 onRemove : function(v, index, r){
26871 this.selections.remove(r);
26875 onRowUpdated : function(v, index, r){
26876 if(this.isSelected(r)){
26877 v.onRowSelect(index);
26883 * @param {Array} records The records to select
26884 * @param {Boolean} keepExisting (optional) True to keep existing selections
26886 selectRecords : function(records, keepExisting)
26889 this.clearSelections();
26891 var ds = this.grid.store;
26892 for(var i = 0, len = records.length; i < len; i++){
26893 this.selectRow(ds.indexOf(records[i]), true);
26898 * Gets the number of selected rows.
26901 getCount : function(){
26902 return this.selections.length;
26906 * Selects the first row in the grid.
26908 selectFirstRow : function(){
26913 * Select the last row.
26914 * @param {Boolean} keepExisting (optional) True to keep existing selections
26916 selectLastRow : function(keepExisting){
26917 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26918 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26922 * Selects the row immediately following the last selected row.
26923 * @param {Boolean} keepExisting (optional) True to keep existing selections
26925 selectNext : function(keepExisting)
26927 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26928 this.selectRow(this.last+1, keepExisting);
26929 this.grid.getView().focusRow(this.last);
26934 * Selects the row that precedes the last selected row.
26935 * @param {Boolean} keepExisting (optional) True to keep existing selections
26937 selectPrevious : function(keepExisting){
26939 this.selectRow(this.last-1, keepExisting);
26940 this.grid.getView().focusRow(this.last);
26945 * Returns the selected records
26946 * @return {Array} Array of selected records
26948 getSelections : function(){
26949 return [].concat(this.selections.items);
26953 * Returns the first selected record.
26956 getSelected : function(){
26957 return this.selections.itemAt(0);
26962 * Clears all selections.
26964 clearSelections : function(fast)
26970 var ds = this.grid.store;
26971 var s = this.selections;
26972 s.each(function(r){
26973 this.deselectRow(ds.indexOfId(r.id));
26977 this.selections.clear();
26984 * Selects all rows.
26986 selectAll : function(){
26990 this.selections.clear();
26991 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26992 this.selectRow(i, true);
26997 * Returns True if there is a selection.
26998 * @return {Boolean}
27000 hasSelection : function(){
27001 return this.selections.length > 0;
27005 * Returns True if the specified row is selected.
27006 * @param {Number/Record} record The record or index of the record to check
27007 * @return {Boolean}
27009 isSelected : function(index){
27010 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27011 return (r && this.selections.key(r.id) ? true : false);
27015 * Returns True if the specified record id is selected.
27016 * @param {String} id The id of record to check
27017 * @return {Boolean}
27019 isIdSelected : function(id){
27020 return (this.selections.key(id) ? true : false);
27025 handleMouseDBClick : function(e, t){
27029 handleMouseDown : function(e, t)
27031 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27032 if(this.isLocked() || rowIndex < 0 ){
27035 if(e.shiftKey && this.last !== false){
27036 var last = this.last;
27037 this.selectRange(last, rowIndex, e.ctrlKey);
27038 this.last = last; // reset the last
27042 var isSelected = this.isSelected(rowIndex);
27043 //Roo.log("select row:" + rowIndex);
27045 this.deselectRow(rowIndex);
27047 this.selectRow(rowIndex, true);
27051 if(e.button !== 0 && isSelected){
27052 alert('rowIndex 2: ' + rowIndex);
27053 view.focusRow(rowIndex);
27054 }else if(e.ctrlKey && isSelected){
27055 this.deselectRow(rowIndex);
27056 }else if(!isSelected){
27057 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27058 view.focusRow(rowIndex);
27062 this.fireEvent("afterselectionchange", this);
27065 handleDragableRowClick : function(grid, rowIndex, e)
27067 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27068 this.selectRow(rowIndex, false);
27069 grid.view.focusRow(rowIndex);
27070 this.fireEvent("afterselectionchange", this);
27075 * Selects multiple rows.
27076 * @param {Array} rows Array of the indexes of the row to select
27077 * @param {Boolean} keepExisting (optional) True to keep existing selections
27079 selectRows : function(rows, keepExisting){
27081 this.clearSelections();
27083 for(var i = 0, len = rows.length; i < len; i++){
27084 this.selectRow(rows[i], true);
27089 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27090 * @param {Number} startRow The index of the first row in the range
27091 * @param {Number} endRow The index of the last row in the range
27092 * @param {Boolean} keepExisting (optional) True to retain existing selections
27094 selectRange : function(startRow, endRow, keepExisting){
27099 this.clearSelections();
27101 if(startRow <= endRow){
27102 for(var i = startRow; i <= endRow; i++){
27103 this.selectRow(i, true);
27106 for(var i = startRow; i >= endRow; i--){
27107 this.selectRow(i, true);
27113 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27114 * @param {Number} startRow The index of the first row in the range
27115 * @param {Number} endRow The index of the last row in the range
27117 deselectRange : function(startRow, endRow, preventViewNotify){
27121 for(var i = startRow; i <= endRow; i++){
27122 this.deselectRow(i, preventViewNotify);
27128 * @param {Number} row The index of the row to select
27129 * @param {Boolean} keepExisting (optional) True to keep existing selections
27131 selectRow : function(index, keepExisting, preventViewNotify)
27133 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27136 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27137 if(!keepExisting || this.singleSelect){
27138 this.clearSelections();
27141 var r = this.grid.store.getAt(index);
27142 //console.log('selectRow - record id :' + r.id);
27144 this.selections.add(r);
27145 this.last = this.lastActive = index;
27146 if(!preventViewNotify){
27147 var proxy = new Roo.Element(
27148 this.grid.getRowDom(index)
27150 proxy.addClass('bg-info info');
27152 this.fireEvent("rowselect", this, index, r);
27153 this.fireEvent("selectionchange", this);
27159 * @param {Number} row The index of the row to deselect
27161 deselectRow : function(index, preventViewNotify)
27166 if(this.last == index){
27169 if(this.lastActive == index){
27170 this.lastActive = false;
27173 var r = this.grid.store.getAt(index);
27178 this.selections.remove(r);
27179 //.console.log('deselectRow - record id :' + r.id);
27180 if(!preventViewNotify){
27182 var proxy = new Roo.Element(
27183 this.grid.getRowDom(index)
27185 proxy.removeClass('bg-info info');
27187 this.fireEvent("rowdeselect", this, index);
27188 this.fireEvent("selectionchange", this);
27192 restoreLast : function(){
27194 this.last = this._last;
27199 acceptsNav : function(row, col, cm){
27200 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27204 onEditorKey : function(field, e){
27205 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27210 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27212 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27214 }else if(k == e.ENTER && !e.ctrlKey){
27218 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27220 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27222 }else if(k == e.ESC){
27226 g.startEditing(newCell[0], newCell[1]);
27232 * Ext JS Library 1.1.1
27233 * Copyright(c) 2006-2007, Ext JS, LLC.
27235 * Originally Released Under LGPL - original licence link has changed is not relivant.
27238 * <script type="text/javascript">
27242 * @class Roo.bootstrap.PagingToolbar
27243 * @extends Roo.bootstrap.NavSimplebar
27244 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27246 * Create a new PagingToolbar
27247 * @param {Object} config The config object
27248 * @param {Roo.data.Store} store
27250 Roo.bootstrap.PagingToolbar = function(config)
27252 // old args format still supported... - xtype is prefered..
27253 // created from xtype...
27255 this.ds = config.dataSource;
27257 if (config.store && !this.ds) {
27258 this.store= Roo.factory(config.store, Roo.data);
27259 this.ds = this.store;
27260 this.ds.xmodule = this.xmodule || false;
27263 this.toolbarItems = [];
27264 if (config.items) {
27265 this.toolbarItems = config.items;
27268 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27273 this.bind(this.ds);
27276 if (Roo.bootstrap.version == 4) {
27277 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27279 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27284 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27286 * @cfg {Roo.data.Store} dataSource
27287 * The underlying data store providing the paged data
27290 * @cfg {String/HTMLElement/Element} container
27291 * container The id or element that will contain the toolbar
27294 * @cfg {Boolean} displayInfo
27295 * True to display the displayMsg (defaults to false)
27298 * @cfg {Number} pageSize
27299 * The number of records to display per page (defaults to 20)
27303 * @cfg {String} displayMsg
27304 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27306 displayMsg : 'Displaying {0} - {1} of {2}',
27308 * @cfg {String} emptyMsg
27309 * The message to display when no records are found (defaults to "No data to display")
27311 emptyMsg : 'No data to display',
27313 * Customizable piece of the default paging text (defaults to "Page")
27316 beforePageText : "Page",
27318 * Customizable piece of the default paging text (defaults to "of %0")
27321 afterPageText : "of {0}",
27323 * Customizable piece of the default paging text (defaults to "First Page")
27326 firstText : "First Page",
27328 * Customizable piece of the default paging text (defaults to "Previous Page")
27331 prevText : "Previous Page",
27333 * Customizable piece of the default paging text (defaults to "Next Page")
27336 nextText : "Next Page",
27338 * Customizable piece of the default paging text (defaults to "Last Page")
27341 lastText : "Last Page",
27343 * Customizable piece of the default paging text (defaults to "Refresh")
27346 refreshText : "Refresh",
27350 onRender : function(ct, position)
27352 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27353 this.navgroup.parentId = this.id;
27354 this.navgroup.onRender(this.el, null);
27355 // add the buttons to the navgroup
27357 if(this.displayInfo){
27358 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27359 this.displayEl = this.el.select('.x-paging-info', true).first();
27360 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27361 // this.displayEl = navel.el.select('span',true).first();
27367 Roo.each(_this.buttons, function(e){ // this might need to use render????
27368 Roo.factory(e).render(_this.el);
27372 Roo.each(_this.toolbarItems, function(e) {
27373 _this.navgroup.addItem(e);
27377 this.first = this.navgroup.addItem({
27378 tooltip: this.firstText,
27379 cls: "prev btn-outline-secondary",
27380 html : ' <i class="fa fa-step-backward"></i>',
27382 preventDefault: true,
27383 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27386 this.prev = this.navgroup.addItem({
27387 tooltip: this.prevText,
27388 cls: "prev btn-outline-secondary",
27389 html : ' <i class="fa fa-backward"></i>',
27391 preventDefault: true,
27392 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27394 //this.addSeparator();
27397 var field = this.navgroup.addItem( {
27399 cls : 'x-paging-position btn-outline-secondary',
27401 html : this.beforePageText +
27402 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27403 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27406 this.field = field.el.select('input', true).first();
27407 this.field.on("keydown", this.onPagingKeydown, this);
27408 this.field.on("focus", function(){this.dom.select();});
27411 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27412 //this.field.setHeight(18);
27413 //this.addSeparator();
27414 this.next = this.navgroup.addItem({
27415 tooltip: this.nextText,
27416 cls: "next btn-outline-secondary",
27417 html : ' <i class="fa fa-forward"></i>',
27419 preventDefault: true,
27420 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27422 this.last = this.navgroup.addItem({
27423 tooltip: this.lastText,
27424 html : ' <i class="fa fa-step-forward"></i>',
27425 cls: "next btn-outline-secondary",
27427 preventDefault: true,
27428 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27430 //this.addSeparator();
27431 this.loading = this.navgroup.addItem({
27432 tooltip: this.refreshText,
27433 cls: "btn-outline-secondary",
27434 html : ' <i class="fa fa-refresh"></i>',
27435 preventDefault: true,
27436 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27442 updateInfo : function(){
27443 if(this.displayEl){
27444 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27445 var msg = count == 0 ?
27449 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27451 this.displayEl.update(msg);
27456 onLoad : function(ds, r, o)
27458 this.cursor = o.params && o.params.start ? o.params.start : 0;
27460 var d = this.getPageData(),
27465 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27466 this.field.dom.value = ap;
27467 this.first.setDisabled(ap == 1);
27468 this.prev.setDisabled(ap == 1);
27469 this.next.setDisabled(ap == ps);
27470 this.last.setDisabled(ap == ps);
27471 this.loading.enable();
27476 getPageData : function(){
27477 var total = this.ds.getTotalCount();
27480 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27481 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27486 onLoadError : function(){
27487 this.loading.enable();
27491 onPagingKeydown : function(e){
27492 var k = e.getKey();
27493 var d = this.getPageData();
27495 var v = this.field.dom.value, pageNum;
27496 if(!v || isNaN(pageNum = parseInt(v, 10))){
27497 this.field.dom.value = d.activePage;
27500 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27501 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27504 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))
27506 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27507 this.field.dom.value = pageNum;
27508 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27511 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27513 var v = this.field.dom.value, pageNum;
27514 var increment = (e.shiftKey) ? 10 : 1;
27515 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27518 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27519 this.field.dom.value = d.activePage;
27522 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27524 this.field.dom.value = parseInt(v, 10) + increment;
27525 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27526 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27533 beforeLoad : function(){
27535 this.loading.disable();
27540 onClick : function(which){
27549 ds.load({params:{start: 0, limit: this.pageSize}});
27552 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27555 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27558 var total = ds.getTotalCount();
27559 var extra = total % this.pageSize;
27560 var lastStart = extra ? (total - extra) : total-this.pageSize;
27561 ds.load({params:{start: lastStart, limit: this.pageSize}});
27564 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27570 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27571 * @param {Roo.data.Store} store The data store to unbind
27573 unbind : function(ds){
27574 ds.un("beforeload", this.beforeLoad, this);
27575 ds.un("load", this.onLoad, this);
27576 ds.un("loadexception", this.onLoadError, this);
27577 ds.un("remove", this.updateInfo, this);
27578 ds.un("add", this.updateInfo, this);
27579 this.ds = undefined;
27583 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27584 * @param {Roo.data.Store} store The data store to bind
27586 bind : function(ds){
27587 ds.on("beforeload", this.beforeLoad, this);
27588 ds.on("load", this.onLoad, this);
27589 ds.on("loadexception", this.onLoadError, this);
27590 ds.on("remove", this.updateInfo, this);
27591 ds.on("add", this.updateInfo, this);
27602 * @class Roo.bootstrap.MessageBar
27603 * @extends Roo.bootstrap.Component
27604 * Bootstrap MessageBar class
27605 * @cfg {String} html contents of the MessageBar
27606 * @cfg {String} weight (info | success | warning | danger) default info
27607 * @cfg {String} beforeClass insert the bar before the given class
27608 * @cfg {Boolean} closable (true | false) default false
27609 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27612 * Create a new Element
27613 * @param {Object} config The config object
27616 Roo.bootstrap.MessageBar = function(config){
27617 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27620 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27626 beforeClass: 'bootstrap-sticky-wrap',
27628 getAutoCreate : function(){
27632 cls: 'alert alert-dismissable alert-' + this.weight,
27637 html: this.html || ''
27643 cfg.cls += ' alert-messages-fixed';
27657 onRender : function(ct, position)
27659 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27662 var cfg = Roo.apply({}, this.getAutoCreate());
27666 cfg.cls += ' ' + this.cls;
27669 cfg.style = this.style;
27671 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27673 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27676 this.el.select('>button.close').on('click', this.hide, this);
27682 if (!this.rendered) {
27688 this.fireEvent('show', this);
27694 if (!this.rendered) {
27700 this.fireEvent('hide', this);
27703 update : function()
27705 // var e = this.el.dom.firstChild;
27707 // if(this.closable){
27708 // e = e.nextSibling;
27711 // e.data = this.html || '';
27713 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27729 * @class Roo.bootstrap.Graph
27730 * @extends Roo.bootstrap.Component
27731 * Bootstrap Graph class
27735 @cfg {String} graphtype bar | vbar | pie
27736 @cfg {number} g_x coodinator | centre x (pie)
27737 @cfg {number} g_y coodinator | centre y (pie)
27738 @cfg {number} g_r radius (pie)
27739 @cfg {number} g_height height of the chart (respected by all elements in the set)
27740 @cfg {number} g_width width of the chart (respected by all elements in the set)
27741 @cfg {Object} title The title of the chart
27744 -opts (object) options for the chart
27746 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27747 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27749 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.
27750 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27752 o stretch (boolean)
27754 -opts (object) options for the pie
27757 o startAngle (number)
27758 o endAngle (number)
27762 * Create a new Input
27763 * @param {Object} config The config object
27766 Roo.bootstrap.Graph = function(config){
27767 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27773 * The img click event for the img.
27774 * @param {Roo.EventObject} e
27780 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27791 //g_colors: this.colors,
27798 getAutoCreate : function(){
27809 onRender : function(ct,position){
27812 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27814 if (typeof(Raphael) == 'undefined') {
27815 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27819 this.raphael = Raphael(this.el.dom);
27821 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27822 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27823 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27824 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27826 r.text(160, 10, "Single Series Chart").attr(txtattr);
27827 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27828 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27829 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27831 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27832 r.barchart(330, 10, 300, 220, data1);
27833 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27834 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27837 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27838 // r.barchart(30, 30, 560, 250, xdata, {
27839 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27840 // axis : "0 0 1 1",
27841 // axisxlabels : xdata
27842 // //yvalues : cols,
27845 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27847 // this.load(null,xdata,{
27848 // axis : "0 0 1 1",
27849 // axisxlabels : xdata
27854 load : function(graphtype,xdata,opts)
27856 this.raphael.clear();
27858 graphtype = this.graphtype;
27863 var r = this.raphael,
27864 fin = function () {
27865 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27867 fout = function () {
27868 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27870 pfin = function() {
27871 this.sector.stop();
27872 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27875 this.label[0].stop();
27876 this.label[0].attr({ r: 7.5 });
27877 this.label[1].attr({ "font-weight": 800 });
27880 pfout = function() {
27881 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27884 this.label[0].animate({ r: 5 }, 500, "bounce");
27885 this.label[1].attr({ "font-weight": 400 });
27891 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27894 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27897 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27898 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27900 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27907 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27912 setTitle: function(o)
27917 initEvents: function() {
27920 this.el.on('click', this.onClick, this);
27924 onClick : function(e)
27926 Roo.log('img onclick');
27927 this.fireEvent('click', this, e);
27939 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27942 * @class Roo.bootstrap.dash.NumberBox
27943 * @extends Roo.bootstrap.Component
27944 * Bootstrap NumberBox class
27945 * @cfg {String} headline Box headline
27946 * @cfg {String} content Box content
27947 * @cfg {String} icon Box icon
27948 * @cfg {String} footer Footer text
27949 * @cfg {String} fhref Footer href
27952 * Create a new NumberBox
27953 * @param {Object} config The config object
27957 Roo.bootstrap.dash.NumberBox = function(config){
27958 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27962 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27971 getAutoCreate : function(){
27975 cls : 'small-box ',
27983 cls : 'roo-headline',
27984 html : this.headline
27988 cls : 'roo-content',
27989 html : this.content
28003 cls : 'ion ' + this.icon
28012 cls : 'small-box-footer',
28013 href : this.fhref || '#',
28017 cfg.cn.push(footer);
28024 onRender : function(ct,position){
28025 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28032 setHeadline: function (value)
28034 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28037 setFooter: function (value, href)
28039 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28042 this.el.select('a.small-box-footer',true).first().attr('href', href);
28047 setContent: function (value)
28049 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28052 initEvents: function()
28066 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28069 * @class Roo.bootstrap.dash.TabBox
28070 * @extends Roo.bootstrap.Component
28071 * Bootstrap TabBox class
28072 * @cfg {String} title Title of the TabBox
28073 * @cfg {String} icon Icon of the TabBox
28074 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28075 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28078 * Create a new TabBox
28079 * @param {Object} config The config object
28083 Roo.bootstrap.dash.TabBox = function(config){
28084 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28089 * When a pane is added
28090 * @param {Roo.bootstrap.dash.TabPane} pane
28094 * @event activatepane
28095 * When a pane is activated
28096 * @param {Roo.bootstrap.dash.TabPane} pane
28098 "activatepane" : true
28106 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28111 tabScrollable : false,
28113 getChildContainer : function()
28115 return this.el.select('.tab-content', true).first();
28118 getAutoCreate : function(){
28122 cls: 'pull-left header',
28130 cls: 'fa ' + this.icon
28136 cls: 'nav nav-tabs pull-right',
28142 if(this.tabScrollable){
28149 cls: 'nav nav-tabs pull-right',
28160 cls: 'nav-tabs-custom',
28165 cls: 'tab-content no-padding',
28173 initEvents : function()
28175 //Roo.log('add add pane handler');
28176 this.on('addpane', this.onAddPane, this);
28179 * Updates the box title
28180 * @param {String} html to set the title to.
28182 setTitle : function(value)
28184 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28186 onAddPane : function(pane)
28188 this.panes.push(pane);
28189 //Roo.log('addpane');
28191 // tabs are rendere left to right..
28192 if(!this.showtabs){
28196 var ctr = this.el.select('.nav-tabs', true).first();
28199 var existing = ctr.select('.nav-tab',true);
28200 var qty = existing.getCount();;
28203 var tab = ctr.createChild({
28205 cls : 'nav-tab' + (qty ? '' : ' active'),
28213 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28216 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28218 pane.el.addClass('active');
28223 onTabClick : function(ev,un,ob,pane)
28225 //Roo.log('tab - prev default');
28226 ev.preventDefault();
28229 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28230 pane.tab.addClass('active');
28231 //Roo.log(pane.title);
28232 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28233 // technically we should have a deactivate event.. but maybe add later.
28234 // and it should not de-activate the selected tab...
28235 this.fireEvent('activatepane', pane);
28236 pane.el.addClass('active');
28237 pane.fireEvent('activate');
28242 getActivePane : function()
28245 Roo.each(this.panes, function(p) {
28246 if(p.el.hasClass('active')){
28267 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28269 * @class Roo.bootstrap.TabPane
28270 * @extends Roo.bootstrap.Component
28271 * Bootstrap TabPane class
28272 * @cfg {Boolean} active (false | true) Default false
28273 * @cfg {String} title title of panel
28277 * Create a new TabPane
28278 * @param {Object} config The config object
28281 Roo.bootstrap.dash.TabPane = function(config){
28282 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28288 * When a pane is activated
28289 * @param {Roo.bootstrap.dash.TabPane} pane
28296 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28301 // the tabBox that this is attached to.
28304 getAutoCreate : function()
28312 cfg.cls += ' active';
28317 initEvents : function()
28319 //Roo.log('trigger add pane handler');
28320 this.parent().fireEvent('addpane', this)
28324 * Updates the tab title
28325 * @param {String} html to set the title to.
28327 setTitle: function(str)
28333 this.tab.select('a', true).first().dom.innerHTML = str;
28350 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28353 * @class Roo.bootstrap.menu.Menu
28354 * @extends Roo.bootstrap.Component
28355 * Bootstrap Menu class - container for Menu
28356 * @cfg {String} html Text of the menu
28357 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28358 * @cfg {String} icon Font awesome icon
28359 * @cfg {String} pos Menu align to (top | bottom) default bottom
28363 * Create a new Menu
28364 * @param {Object} config The config object
28368 Roo.bootstrap.menu.Menu = function(config){
28369 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28373 * @event beforeshow
28374 * Fires before this menu is displayed
28375 * @param {Roo.bootstrap.menu.Menu} this
28379 * @event beforehide
28380 * Fires before this menu is hidden
28381 * @param {Roo.bootstrap.menu.Menu} this
28386 * Fires after this menu is displayed
28387 * @param {Roo.bootstrap.menu.Menu} this
28392 * Fires after this menu is hidden
28393 * @param {Roo.bootstrap.menu.Menu} this
28398 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28399 * @param {Roo.bootstrap.menu.Menu} this
28400 * @param {Roo.EventObject} e
28407 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28411 weight : 'default',
28416 getChildContainer : function() {
28417 if(this.isSubMenu){
28421 return this.el.select('ul.dropdown-menu', true).first();
28424 getAutoCreate : function()
28429 cls : 'roo-menu-text',
28437 cls : 'fa ' + this.icon
28448 cls : 'dropdown-button btn btn-' + this.weight,
28453 cls : 'dropdown-toggle btn btn-' + this.weight,
28463 cls : 'dropdown-menu'
28469 if(this.pos == 'top'){
28470 cfg.cls += ' dropup';
28473 if(this.isSubMenu){
28476 cls : 'dropdown-menu'
28483 onRender : function(ct, position)
28485 this.isSubMenu = ct.hasClass('dropdown-submenu');
28487 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28490 initEvents : function()
28492 if(this.isSubMenu){
28496 this.hidden = true;
28498 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28499 this.triggerEl.on('click', this.onTriggerPress, this);
28501 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28502 this.buttonEl.on('click', this.onClick, this);
28508 if(this.isSubMenu){
28512 return this.el.select('ul.dropdown-menu', true).first();
28515 onClick : function(e)
28517 this.fireEvent("click", this, e);
28520 onTriggerPress : function(e)
28522 if (this.isVisible()) {
28529 isVisible : function(){
28530 return !this.hidden;
28535 this.fireEvent("beforeshow", this);
28537 this.hidden = false;
28538 this.el.addClass('open');
28540 Roo.get(document).on("mouseup", this.onMouseUp, this);
28542 this.fireEvent("show", this);
28549 this.fireEvent("beforehide", this);
28551 this.hidden = true;
28552 this.el.removeClass('open');
28554 Roo.get(document).un("mouseup", this.onMouseUp);
28556 this.fireEvent("hide", this);
28559 onMouseUp : function()
28573 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28576 * @class Roo.bootstrap.menu.Item
28577 * @extends Roo.bootstrap.Component
28578 * Bootstrap MenuItem class
28579 * @cfg {Boolean} submenu (true | false) default false
28580 * @cfg {String} html text of the item
28581 * @cfg {String} href the link
28582 * @cfg {Boolean} disable (true | false) default false
28583 * @cfg {Boolean} preventDefault (true | false) default true
28584 * @cfg {String} icon Font awesome icon
28585 * @cfg {String} pos Submenu align to (left | right) default right
28589 * Create a new Item
28590 * @param {Object} config The config object
28594 Roo.bootstrap.menu.Item = function(config){
28595 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28599 * Fires when the mouse is hovering over this menu
28600 * @param {Roo.bootstrap.menu.Item} this
28601 * @param {Roo.EventObject} e
28606 * Fires when the mouse exits this menu
28607 * @param {Roo.bootstrap.menu.Item} this
28608 * @param {Roo.EventObject} e
28614 * The raw click event for the entire grid.
28615 * @param {Roo.EventObject} e
28621 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28626 preventDefault: true,
28631 getAutoCreate : function()
28636 cls : 'roo-menu-item-text',
28644 cls : 'fa ' + this.icon
28653 href : this.href || '#',
28660 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28664 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28666 if(this.pos == 'left'){
28667 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28674 initEvents : function()
28676 this.el.on('mouseover', this.onMouseOver, this);
28677 this.el.on('mouseout', this.onMouseOut, this);
28679 this.el.select('a', true).first().on('click', this.onClick, this);
28683 onClick : function(e)
28685 if(this.preventDefault){
28686 e.preventDefault();
28689 this.fireEvent("click", this, e);
28692 onMouseOver : function(e)
28694 if(this.submenu && this.pos == 'left'){
28695 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28698 this.fireEvent("mouseover", this, e);
28701 onMouseOut : function(e)
28703 this.fireEvent("mouseout", this, e);
28715 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28718 * @class Roo.bootstrap.menu.Separator
28719 * @extends Roo.bootstrap.Component
28720 * Bootstrap Separator class
28723 * Create a new Separator
28724 * @param {Object} config The config object
28728 Roo.bootstrap.menu.Separator = function(config){
28729 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28732 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28734 getAutoCreate : function(){
28755 * @class Roo.bootstrap.Tooltip
28756 * Bootstrap Tooltip class
28757 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28758 * to determine which dom element triggers the tooltip.
28760 * It needs to add support for additional attributes like tooltip-position
28763 * Create a new Toolti
28764 * @param {Object} config The config object
28767 Roo.bootstrap.Tooltip = function(config){
28768 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28770 this.alignment = Roo.bootstrap.Tooltip.alignment;
28772 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28773 this.alignment = config.alignment;
28778 Roo.apply(Roo.bootstrap.Tooltip, {
28780 * @function init initialize tooltip monitoring.
28784 currentTip : false,
28785 currentRegion : false,
28791 Roo.get(document).on('mouseover', this.enter ,this);
28792 Roo.get(document).on('mouseout', this.leave, this);
28795 this.currentTip = new Roo.bootstrap.Tooltip();
28798 enter : function(ev)
28800 var dom = ev.getTarget();
28802 //Roo.log(['enter',dom]);
28803 var el = Roo.fly(dom);
28804 if (this.currentEl) {
28806 //Roo.log(this.currentEl);
28807 //Roo.log(this.currentEl.contains(dom));
28808 if (this.currentEl == el) {
28811 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28817 if (this.currentTip.el) {
28818 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28822 if(!el || el.dom == document){
28828 // you can not look for children, as if el is the body.. then everythign is the child..
28829 if (!el.attr('tooltip')) { //
28830 if (!el.select("[tooltip]").elements.length) {
28833 // is the mouse over this child...?
28834 bindEl = el.select("[tooltip]").first();
28835 var xy = ev.getXY();
28836 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28837 //Roo.log("not in region.");
28840 //Roo.log("child element over..");
28843 this.currentEl = bindEl;
28844 this.currentTip.bind(bindEl);
28845 this.currentRegion = Roo.lib.Region.getRegion(dom);
28846 this.currentTip.enter();
28849 leave : function(ev)
28851 var dom = ev.getTarget();
28852 //Roo.log(['leave',dom]);
28853 if (!this.currentEl) {
28858 if (dom != this.currentEl.dom) {
28861 var xy = ev.getXY();
28862 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28865 // only activate leave if mouse cursor is outside... bounding box..
28870 if (this.currentTip) {
28871 this.currentTip.leave();
28873 //Roo.log('clear currentEl');
28874 this.currentEl = false;
28879 'left' : ['r-l', [-2,0], 'right'],
28880 'right' : ['l-r', [2,0], 'left'],
28881 'bottom' : ['t-b', [0,2], 'top'],
28882 'top' : [ 'b-t', [0,-2], 'bottom']
28888 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28893 delay : null, // can be { show : 300 , hide: 500}
28897 hoverState : null, //???
28899 placement : 'bottom',
28903 getAutoCreate : function(){
28910 cls : 'tooltip-arrow arrow'
28913 cls : 'tooltip-inner'
28920 bind : function(el)
28925 initEvents : function()
28927 this.arrowEl = this.el.select('.arrow', true).first();
28928 this.innerEl = this.el.select('.tooltip-inner', true).first();
28931 enter : function () {
28933 if (this.timeout != null) {
28934 clearTimeout(this.timeout);
28937 this.hoverState = 'in';
28938 //Roo.log("enter - show");
28939 if (!this.delay || !this.delay.show) {
28944 this.timeout = setTimeout(function () {
28945 if (_t.hoverState == 'in') {
28948 }, this.delay.show);
28952 clearTimeout(this.timeout);
28954 this.hoverState = 'out';
28955 if (!this.delay || !this.delay.hide) {
28961 this.timeout = setTimeout(function () {
28962 //Roo.log("leave - timeout");
28964 if (_t.hoverState == 'out') {
28966 Roo.bootstrap.Tooltip.currentEl = false;
28971 show : function (msg)
28974 this.render(document.body);
28977 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28979 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28981 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28983 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28984 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28986 var placement = typeof this.placement == 'function' ?
28987 this.placement.call(this, this.el, on_el) :
28990 var autoToken = /\s?auto?\s?/i;
28991 var autoPlace = autoToken.test(placement);
28993 placement = placement.replace(autoToken, '') || 'top';
28997 //this.el.setXY([0,0]);
28999 //this.el.dom.style.display='block';
29001 //this.el.appendTo(on_el);
29003 var p = this.getPosition();
29004 var box = this.el.getBox();
29010 var align = this.alignment[placement];
29012 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29014 if(placement == 'top' || placement == 'bottom'){
29016 placement = 'right';
29019 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29020 placement = 'left';
29023 var scroll = Roo.select('body', true).first().getScroll();
29025 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29029 align = this.alignment[placement];
29031 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29035 this.el.alignTo(this.bindEl, align[0],align[1]);
29036 //var arrow = this.el.select('.arrow',true).first();
29037 //arrow.set(align[2],
29039 this.el.addClass(placement);
29040 this.el.addClass("bs-tooltip-"+ placement);
29042 this.el.addClass('in fade show');
29044 this.hoverState = null;
29046 if (this.el.hasClass('fade')) {
29061 //this.el.setXY([0,0]);
29062 this.el.removeClass(['show', 'in']);
29078 * @class Roo.bootstrap.LocationPicker
29079 * @extends Roo.bootstrap.Component
29080 * Bootstrap LocationPicker class
29081 * @cfg {Number} latitude Position when init default 0
29082 * @cfg {Number} longitude Position when init default 0
29083 * @cfg {Number} zoom default 15
29084 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29085 * @cfg {Boolean} mapTypeControl default false
29086 * @cfg {Boolean} disableDoubleClickZoom default false
29087 * @cfg {Boolean} scrollwheel default true
29088 * @cfg {Boolean} streetViewControl default false
29089 * @cfg {Number} radius default 0
29090 * @cfg {String} locationName
29091 * @cfg {Boolean} draggable default true
29092 * @cfg {Boolean} enableAutocomplete default false
29093 * @cfg {Boolean} enableReverseGeocode default true
29094 * @cfg {String} markerTitle
29097 * Create a new LocationPicker
29098 * @param {Object} config The config object
29102 Roo.bootstrap.LocationPicker = function(config){
29104 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29109 * Fires when the picker initialized.
29110 * @param {Roo.bootstrap.LocationPicker} this
29111 * @param {Google Location} location
29115 * @event positionchanged
29116 * Fires when the picker position changed.
29117 * @param {Roo.bootstrap.LocationPicker} this
29118 * @param {Google Location} location
29120 positionchanged : true,
29123 * Fires when the map resize.
29124 * @param {Roo.bootstrap.LocationPicker} this
29129 * Fires when the map show.
29130 * @param {Roo.bootstrap.LocationPicker} this
29135 * Fires when the map hide.
29136 * @param {Roo.bootstrap.LocationPicker} this
29141 * Fires when click the map.
29142 * @param {Roo.bootstrap.LocationPicker} this
29143 * @param {Map event} e
29147 * @event mapRightClick
29148 * Fires when right click the map.
29149 * @param {Roo.bootstrap.LocationPicker} this
29150 * @param {Map event} e
29152 mapRightClick : true,
29154 * @event markerClick
29155 * Fires when click the marker.
29156 * @param {Roo.bootstrap.LocationPicker} this
29157 * @param {Map event} e
29159 markerClick : true,
29161 * @event markerRightClick
29162 * Fires when right click the marker.
29163 * @param {Roo.bootstrap.LocationPicker} this
29164 * @param {Map event} e
29166 markerRightClick : true,
29168 * @event OverlayViewDraw
29169 * Fires when OverlayView Draw
29170 * @param {Roo.bootstrap.LocationPicker} this
29172 OverlayViewDraw : true,
29174 * @event OverlayViewOnAdd
29175 * Fires when OverlayView Draw
29176 * @param {Roo.bootstrap.LocationPicker} this
29178 OverlayViewOnAdd : true,
29180 * @event OverlayViewOnRemove
29181 * Fires when OverlayView Draw
29182 * @param {Roo.bootstrap.LocationPicker} this
29184 OverlayViewOnRemove : true,
29186 * @event OverlayViewShow
29187 * Fires when OverlayView Draw
29188 * @param {Roo.bootstrap.LocationPicker} this
29189 * @param {Pixel} cpx
29191 OverlayViewShow : true,
29193 * @event OverlayViewHide
29194 * Fires when OverlayView Draw
29195 * @param {Roo.bootstrap.LocationPicker} this
29197 OverlayViewHide : true,
29199 * @event loadexception
29200 * Fires when load google lib failed.
29201 * @param {Roo.bootstrap.LocationPicker} this
29203 loadexception : true
29208 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29210 gMapContext: false,
29216 mapTypeControl: false,
29217 disableDoubleClickZoom: false,
29219 streetViewControl: false,
29223 enableAutocomplete: false,
29224 enableReverseGeocode: true,
29227 getAutoCreate: function()
29232 cls: 'roo-location-picker'
29238 initEvents: function(ct, position)
29240 if(!this.el.getWidth() || this.isApplied()){
29244 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29249 initial: function()
29251 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29252 this.fireEvent('loadexception', this);
29256 if(!this.mapTypeId){
29257 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29260 this.gMapContext = this.GMapContext();
29262 this.initOverlayView();
29264 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29268 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29269 _this.setPosition(_this.gMapContext.marker.position);
29272 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29273 _this.fireEvent('mapClick', this, event);
29277 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29278 _this.fireEvent('mapRightClick', this, event);
29282 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29283 _this.fireEvent('markerClick', this, event);
29287 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29288 _this.fireEvent('markerRightClick', this, event);
29292 this.setPosition(this.gMapContext.location);
29294 this.fireEvent('initial', this, this.gMapContext.location);
29297 initOverlayView: function()
29301 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29305 _this.fireEvent('OverlayViewDraw', _this);
29310 _this.fireEvent('OverlayViewOnAdd', _this);
29313 onRemove: function()
29315 _this.fireEvent('OverlayViewOnRemove', _this);
29318 show: function(cpx)
29320 _this.fireEvent('OverlayViewShow', _this, cpx);
29325 _this.fireEvent('OverlayViewHide', _this);
29331 fromLatLngToContainerPixel: function(event)
29333 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29336 isApplied: function()
29338 return this.getGmapContext() == false ? false : true;
29341 getGmapContext: function()
29343 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29346 GMapContext: function()
29348 var position = new google.maps.LatLng(this.latitude, this.longitude);
29350 var _map = new google.maps.Map(this.el.dom, {
29353 mapTypeId: this.mapTypeId,
29354 mapTypeControl: this.mapTypeControl,
29355 disableDoubleClickZoom: this.disableDoubleClickZoom,
29356 scrollwheel: this.scrollwheel,
29357 streetViewControl: this.streetViewControl,
29358 locationName: this.locationName,
29359 draggable: this.draggable,
29360 enableAutocomplete: this.enableAutocomplete,
29361 enableReverseGeocode: this.enableReverseGeocode
29364 var _marker = new google.maps.Marker({
29365 position: position,
29367 title: this.markerTitle,
29368 draggable: this.draggable
29375 location: position,
29376 radius: this.radius,
29377 locationName: this.locationName,
29378 addressComponents: {
29379 formatted_address: null,
29380 addressLine1: null,
29381 addressLine2: null,
29383 streetNumber: null,
29387 stateOrProvince: null
29390 domContainer: this.el.dom,
29391 geodecoder: new google.maps.Geocoder()
29395 drawCircle: function(center, radius, options)
29397 if (this.gMapContext.circle != null) {
29398 this.gMapContext.circle.setMap(null);
29402 options = Roo.apply({}, options, {
29403 strokeColor: "#0000FF",
29404 strokeOpacity: .35,
29406 fillColor: "#0000FF",
29410 options.map = this.gMapContext.map;
29411 options.radius = radius;
29412 options.center = center;
29413 this.gMapContext.circle = new google.maps.Circle(options);
29414 return this.gMapContext.circle;
29420 setPosition: function(location)
29422 this.gMapContext.location = location;
29423 this.gMapContext.marker.setPosition(location);
29424 this.gMapContext.map.panTo(location);
29425 this.drawCircle(location, this.gMapContext.radius, {});
29429 if (this.gMapContext.settings.enableReverseGeocode) {
29430 this.gMapContext.geodecoder.geocode({
29431 latLng: this.gMapContext.location
29432 }, function(results, status) {
29434 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29435 _this.gMapContext.locationName = results[0].formatted_address;
29436 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29438 _this.fireEvent('positionchanged', this, location);
29445 this.fireEvent('positionchanged', this, location);
29450 google.maps.event.trigger(this.gMapContext.map, "resize");
29452 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29454 this.fireEvent('resize', this);
29457 setPositionByLatLng: function(latitude, longitude)
29459 this.setPosition(new google.maps.LatLng(latitude, longitude));
29462 getCurrentPosition: function()
29465 latitude: this.gMapContext.location.lat(),
29466 longitude: this.gMapContext.location.lng()
29470 getAddressName: function()
29472 return this.gMapContext.locationName;
29475 getAddressComponents: function()
29477 return this.gMapContext.addressComponents;
29480 address_component_from_google_geocode: function(address_components)
29484 for (var i = 0; i < address_components.length; i++) {
29485 var component = address_components[i];
29486 if (component.types.indexOf("postal_code") >= 0) {
29487 result.postalCode = component.short_name;
29488 } else if (component.types.indexOf("street_number") >= 0) {
29489 result.streetNumber = component.short_name;
29490 } else if (component.types.indexOf("route") >= 0) {
29491 result.streetName = component.short_name;
29492 } else if (component.types.indexOf("neighborhood") >= 0) {
29493 result.city = component.short_name;
29494 } else if (component.types.indexOf("locality") >= 0) {
29495 result.city = component.short_name;
29496 } else if (component.types.indexOf("sublocality") >= 0) {
29497 result.district = component.short_name;
29498 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29499 result.stateOrProvince = component.short_name;
29500 } else if (component.types.indexOf("country") >= 0) {
29501 result.country = component.short_name;
29505 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29506 result.addressLine2 = "";
29510 setZoomLevel: function(zoom)
29512 this.gMapContext.map.setZoom(zoom);
29525 this.fireEvent('show', this);
29536 this.fireEvent('hide', this);
29541 Roo.apply(Roo.bootstrap.LocationPicker, {
29543 OverlayView : function(map, options)
29545 options = options || {};
29552 * @class Roo.bootstrap.Alert
29553 * @extends Roo.bootstrap.Component
29554 * Bootstrap Alert class - shows an alert area box
29556 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29557 Enter a valid email address
29560 * @cfg {String} title The title of alert
29561 * @cfg {String} html The content of alert
29562 * @cfg {String} weight ( success | info | warning | danger )
29563 * @cfg {String} faicon font-awesomeicon
29566 * Create a new alert
29567 * @param {Object} config The config object
29571 Roo.bootstrap.Alert = function(config){
29572 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29576 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29583 getAutoCreate : function()
29592 cls : 'roo-alert-icon'
29597 cls : 'roo-alert-title',
29602 cls : 'roo-alert-text',
29609 cfg.cn[0].cls += ' fa ' + this.faicon;
29613 cfg.cls += ' alert-' + this.weight;
29619 initEvents: function()
29621 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29624 setTitle : function(str)
29626 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29629 setText : function(str)
29631 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29634 setWeight : function(weight)
29637 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29640 this.weight = weight;
29642 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29645 setIcon : function(icon)
29648 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29651 this.faicon = icon;
29653 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29674 * @class Roo.bootstrap.UploadCropbox
29675 * @extends Roo.bootstrap.Component
29676 * Bootstrap UploadCropbox class
29677 * @cfg {String} emptyText show when image has been loaded
29678 * @cfg {String} rotateNotify show when image too small to rotate
29679 * @cfg {Number} errorTimeout default 3000
29680 * @cfg {Number} minWidth default 300
29681 * @cfg {Number} minHeight default 300
29682 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29683 * @cfg {Boolean} isDocument (true|false) default false
29684 * @cfg {String} url action url
29685 * @cfg {String} paramName default 'imageUpload'
29686 * @cfg {String} method default POST
29687 * @cfg {Boolean} loadMask (true|false) default true
29688 * @cfg {Boolean} loadingText default 'Loading...'
29691 * Create a new UploadCropbox
29692 * @param {Object} config The config object
29695 Roo.bootstrap.UploadCropbox = function(config){
29696 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29700 * @event beforeselectfile
29701 * Fire before select file
29702 * @param {Roo.bootstrap.UploadCropbox} this
29704 "beforeselectfile" : true,
29707 * Fire after initEvent
29708 * @param {Roo.bootstrap.UploadCropbox} this
29713 * Fire after initEvent
29714 * @param {Roo.bootstrap.UploadCropbox} this
29715 * @param {String} data
29720 * Fire when preparing the file data
29721 * @param {Roo.bootstrap.UploadCropbox} this
29722 * @param {Object} file
29727 * Fire when get exception
29728 * @param {Roo.bootstrap.UploadCropbox} this
29729 * @param {XMLHttpRequest} xhr
29731 "exception" : true,
29733 * @event beforeloadcanvas
29734 * Fire before load the canvas
29735 * @param {Roo.bootstrap.UploadCropbox} this
29736 * @param {String} src
29738 "beforeloadcanvas" : true,
29741 * Fire when trash image
29742 * @param {Roo.bootstrap.UploadCropbox} this
29747 * Fire when download the image
29748 * @param {Roo.bootstrap.UploadCropbox} this
29752 * @event footerbuttonclick
29753 * Fire when footerbuttonclick
29754 * @param {Roo.bootstrap.UploadCropbox} this
29755 * @param {String} type
29757 "footerbuttonclick" : true,
29761 * @param {Roo.bootstrap.UploadCropbox} this
29766 * Fire when rotate the image
29767 * @param {Roo.bootstrap.UploadCropbox} this
29768 * @param {String} pos
29773 * Fire when inspect the file
29774 * @param {Roo.bootstrap.UploadCropbox} this
29775 * @param {Object} file
29780 * Fire when xhr upload the file
29781 * @param {Roo.bootstrap.UploadCropbox} this
29782 * @param {Object} data
29787 * Fire when arrange the file data
29788 * @param {Roo.bootstrap.UploadCropbox} this
29789 * @param {Object} formData
29794 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29797 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29799 emptyText : 'Click to upload image',
29800 rotateNotify : 'Image is too small to rotate',
29801 errorTimeout : 3000,
29815 cropType : 'image/jpeg',
29817 canvasLoaded : false,
29818 isDocument : false,
29820 paramName : 'imageUpload',
29822 loadingText : 'Loading...',
29825 getAutoCreate : function()
29829 cls : 'roo-upload-cropbox',
29833 cls : 'roo-upload-cropbox-selector',
29838 cls : 'roo-upload-cropbox-body',
29839 style : 'cursor:pointer',
29843 cls : 'roo-upload-cropbox-preview'
29847 cls : 'roo-upload-cropbox-thumb'
29851 cls : 'roo-upload-cropbox-empty-notify',
29852 html : this.emptyText
29856 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29857 html : this.rotateNotify
29863 cls : 'roo-upload-cropbox-footer',
29866 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29876 onRender : function(ct, position)
29878 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29880 if (this.buttons.length) {
29882 Roo.each(this.buttons, function(bb) {
29884 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29886 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29892 this.maskEl = this.el;
29896 initEvents : function()
29898 this.urlAPI = (window.createObjectURL && window) ||
29899 (window.URL && URL.revokeObjectURL && URL) ||
29900 (window.webkitURL && webkitURL);
29902 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29903 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29905 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29906 this.selectorEl.hide();
29908 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29909 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29911 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29912 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29913 this.thumbEl.hide();
29915 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29916 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29918 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29919 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29920 this.errorEl.hide();
29922 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29923 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29924 this.footerEl.hide();
29926 this.setThumbBoxSize();
29932 this.fireEvent('initial', this);
29939 window.addEventListener("resize", function() { _this.resize(); } );
29941 this.bodyEl.on('click', this.beforeSelectFile, this);
29944 this.bodyEl.on('touchstart', this.onTouchStart, this);
29945 this.bodyEl.on('touchmove', this.onTouchMove, this);
29946 this.bodyEl.on('touchend', this.onTouchEnd, this);
29950 this.bodyEl.on('mousedown', this.onMouseDown, this);
29951 this.bodyEl.on('mousemove', this.onMouseMove, this);
29952 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29953 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29954 Roo.get(document).on('mouseup', this.onMouseUp, this);
29957 this.selectorEl.on('change', this.onFileSelected, this);
29963 this.baseScale = 1;
29965 this.baseRotate = 1;
29966 this.dragable = false;
29967 this.pinching = false;
29970 this.cropData = false;
29971 this.notifyEl.dom.innerHTML = this.emptyText;
29973 this.selectorEl.dom.value = '';
29977 resize : function()
29979 if(this.fireEvent('resize', this) != false){
29980 this.setThumbBoxPosition();
29981 this.setCanvasPosition();
29985 onFooterButtonClick : function(e, el, o, type)
29988 case 'rotate-left' :
29989 this.onRotateLeft(e);
29991 case 'rotate-right' :
29992 this.onRotateRight(e);
29995 this.beforeSelectFile(e);
30010 this.fireEvent('footerbuttonclick', this, type);
30013 beforeSelectFile : function(e)
30015 e.preventDefault();
30017 if(this.fireEvent('beforeselectfile', this) != false){
30018 this.selectorEl.dom.click();
30022 onFileSelected : function(e)
30024 e.preventDefault();
30026 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30030 var file = this.selectorEl.dom.files[0];
30032 if(this.fireEvent('inspect', this, file) != false){
30033 this.prepare(file);
30038 trash : function(e)
30040 this.fireEvent('trash', this);
30043 download : function(e)
30045 this.fireEvent('download', this);
30048 loadCanvas : function(src)
30050 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30054 this.imageEl = document.createElement('img');
30058 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30060 this.imageEl.src = src;
30064 onLoadCanvas : function()
30066 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30067 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30069 this.bodyEl.un('click', this.beforeSelectFile, this);
30071 this.notifyEl.hide();
30072 this.thumbEl.show();
30073 this.footerEl.show();
30075 this.baseRotateLevel();
30077 if(this.isDocument){
30078 this.setThumbBoxSize();
30081 this.setThumbBoxPosition();
30083 this.baseScaleLevel();
30089 this.canvasLoaded = true;
30092 this.maskEl.unmask();
30097 setCanvasPosition : function()
30099 if(!this.canvasEl){
30103 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30104 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30106 this.previewEl.setLeft(pw);
30107 this.previewEl.setTop(ph);
30111 onMouseDown : function(e)
30115 this.dragable = true;
30116 this.pinching = false;
30118 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30119 this.dragable = false;
30123 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30124 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30128 onMouseMove : function(e)
30132 if(!this.canvasLoaded){
30136 if (!this.dragable){
30140 var minX = Math.ceil(this.thumbEl.getLeft(true));
30141 var minY = Math.ceil(this.thumbEl.getTop(true));
30143 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30144 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30146 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30147 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30149 x = x - this.mouseX;
30150 y = y - this.mouseY;
30152 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30153 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30155 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30156 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30158 this.previewEl.setLeft(bgX);
30159 this.previewEl.setTop(bgY);
30161 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30162 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30165 onMouseUp : function(e)
30169 this.dragable = false;
30172 onMouseWheel : function(e)
30176 this.startScale = this.scale;
30178 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30180 if(!this.zoomable()){
30181 this.scale = this.startScale;
30190 zoomable : function()
30192 var minScale = this.thumbEl.getWidth() / this.minWidth;
30194 if(this.minWidth < this.minHeight){
30195 minScale = this.thumbEl.getHeight() / this.minHeight;
30198 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30199 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30203 (this.rotate == 0 || this.rotate == 180) &&
30205 width > this.imageEl.OriginWidth ||
30206 height > this.imageEl.OriginHeight ||
30207 (width < this.minWidth && height < this.minHeight)
30215 (this.rotate == 90 || this.rotate == 270) &&
30217 width > this.imageEl.OriginWidth ||
30218 height > this.imageEl.OriginHeight ||
30219 (width < this.minHeight && height < this.minWidth)
30226 !this.isDocument &&
30227 (this.rotate == 0 || this.rotate == 180) &&
30229 width < this.minWidth ||
30230 width > this.imageEl.OriginWidth ||
30231 height < this.minHeight ||
30232 height > this.imageEl.OriginHeight
30239 !this.isDocument &&
30240 (this.rotate == 90 || this.rotate == 270) &&
30242 width < this.minHeight ||
30243 width > this.imageEl.OriginWidth ||
30244 height < this.minWidth ||
30245 height > this.imageEl.OriginHeight
30255 onRotateLeft : function(e)
30257 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30259 var minScale = this.thumbEl.getWidth() / this.minWidth;
30261 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30262 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30264 this.startScale = this.scale;
30266 while (this.getScaleLevel() < minScale){
30268 this.scale = this.scale + 1;
30270 if(!this.zoomable()){
30275 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30276 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30281 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30288 this.scale = this.startScale;
30290 this.onRotateFail();
30295 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30297 if(this.isDocument){
30298 this.setThumbBoxSize();
30299 this.setThumbBoxPosition();
30300 this.setCanvasPosition();
30305 this.fireEvent('rotate', this, 'left');
30309 onRotateRight : function(e)
30311 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30313 var minScale = this.thumbEl.getWidth() / this.minWidth;
30315 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30316 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30318 this.startScale = this.scale;
30320 while (this.getScaleLevel() < minScale){
30322 this.scale = this.scale + 1;
30324 if(!this.zoomable()){
30329 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30330 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30335 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30342 this.scale = this.startScale;
30344 this.onRotateFail();
30349 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30351 if(this.isDocument){
30352 this.setThumbBoxSize();
30353 this.setThumbBoxPosition();
30354 this.setCanvasPosition();
30359 this.fireEvent('rotate', this, 'right');
30362 onRotateFail : function()
30364 this.errorEl.show(true);
30368 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30373 this.previewEl.dom.innerHTML = '';
30375 var canvasEl = document.createElement("canvas");
30377 var contextEl = canvasEl.getContext("2d");
30379 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30380 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30381 var center = this.imageEl.OriginWidth / 2;
30383 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30384 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30385 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30386 center = this.imageEl.OriginHeight / 2;
30389 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30391 contextEl.translate(center, center);
30392 contextEl.rotate(this.rotate * Math.PI / 180);
30394 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30396 this.canvasEl = document.createElement("canvas");
30398 this.contextEl = this.canvasEl.getContext("2d");
30400 switch (this.rotate) {
30403 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30404 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30406 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30411 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30412 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30414 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30415 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);
30419 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30424 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30425 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30427 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30428 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);
30432 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);
30437 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30438 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30440 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30441 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30445 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);
30452 this.previewEl.appendChild(this.canvasEl);
30454 this.setCanvasPosition();
30459 if(!this.canvasLoaded){
30463 var imageCanvas = document.createElement("canvas");
30465 var imageContext = imageCanvas.getContext("2d");
30467 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30468 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30470 var center = imageCanvas.width / 2;
30472 imageContext.translate(center, center);
30474 imageContext.rotate(this.rotate * Math.PI / 180);
30476 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30478 var canvas = document.createElement("canvas");
30480 var context = canvas.getContext("2d");
30482 canvas.width = this.minWidth;
30483 canvas.height = this.minHeight;
30485 switch (this.rotate) {
30488 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30489 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30491 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30492 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30494 var targetWidth = this.minWidth - 2 * x;
30495 var targetHeight = this.minHeight - 2 * y;
30499 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30500 scale = targetWidth / width;
30503 if(x > 0 && y == 0){
30504 scale = targetHeight / height;
30507 if(x > 0 && y > 0){
30508 scale = targetWidth / width;
30510 if(width < height){
30511 scale = targetHeight / height;
30515 context.scale(scale, scale);
30517 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30518 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30520 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30521 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30523 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30528 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30529 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30531 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30532 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30534 var targetWidth = this.minWidth - 2 * x;
30535 var targetHeight = this.minHeight - 2 * y;
30539 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30540 scale = targetWidth / width;
30543 if(x > 0 && y == 0){
30544 scale = targetHeight / height;
30547 if(x > 0 && y > 0){
30548 scale = targetWidth / width;
30550 if(width < height){
30551 scale = targetHeight / height;
30555 context.scale(scale, scale);
30557 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30558 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30560 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30561 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30563 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30565 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30570 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30571 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30573 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30574 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30576 var targetWidth = this.minWidth - 2 * x;
30577 var targetHeight = this.minHeight - 2 * y;
30581 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30582 scale = targetWidth / width;
30585 if(x > 0 && y == 0){
30586 scale = targetHeight / height;
30589 if(x > 0 && y > 0){
30590 scale = targetWidth / width;
30592 if(width < height){
30593 scale = targetHeight / height;
30597 context.scale(scale, scale);
30599 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30600 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30602 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30603 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30605 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30606 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30608 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30613 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30614 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30616 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30617 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30619 var targetWidth = this.minWidth - 2 * x;
30620 var targetHeight = this.minHeight - 2 * y;
30624 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30625 scale = targetWidth / width;
30628 if(x > 0 && y == 0){
30629 scale = targetHeight / height;
30632 if(x > 0 && y > 0){
30633 scale = targetWidth / width;
30635 if(width < height){
30636 scale = targetHeight / height;
30640 context.scale(scale, scale);
30642 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30643 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30645 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30646 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30648 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30650 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30657 this.cropData = canvas.toDataURL(this.cropType);
30659 if(this.fireEvent('crop', this, this.cropData) !== false){
30660 this.process(this.file, this.cropData);
30667 setThumbBoxSize : function()
30671 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30672 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30673 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30675 this.minWidth = width;
30676 this.minHeight = height;
30678 if(this.rotate == 90 || this.rotate == 270){
30679 this.minWidth = height;
30680 this.minHeight = width;
30685 width = Math.ceil(this.minWidth * height / this.minHeight);
30687 if(this.minWidth > this.minHeight){
30689 height = Math.ceil(this.minHeight * width / this.minWidth);
30692 this.thumbEl.setStyle({
30693 width : width + 'px',
30694 height : height + 'px'
30701 setThumbBoxPosition : function()
30703 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30704 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30706 this.thumbEl.setLeft(x);
30707 this.thumbEl.setTop(y);
30711 baseRotateLevel : function()
30713 this.baseRotate = 1;
30716 typeof(this.exif) != 'undefined' &&
30717 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30718 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30720 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30723 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30727 baseScaleLevel : function()
30731 if(this.isDocument){
30733 if(this.baseRotate == 6 || this.baseRotate == 8){
30735 height = this.thumbEl.getHeight();
30736 this.baseScale = height / this.imageEl.OriginWidth;
30738 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30739 width = this.thumbEl.getWidth();
30740 this.baseScale = width / this.imageEl.OriginHeight;
30746 height = this.thumbEl.getHeight();
30747 this.baseScale = height / this.imageEl.OriginHeight;
30749 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30750 width = this.thumbEl.getWidth();
30751 this.baseScale = width / this.imageEl.OriginWidth;
30757 if(this.baseRotate == 6 || this.baseRotate == 8){
30759 width = this.thumbEl.getHeight();
30760 this.baseScale = width / this.imageEl.OriginHeight;
30762 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30763 height = this.thumbEl.getWidth();
30764 this.baseScale = height / this.imageEl.OriginHeight;
30767 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30768 height = this.thumbEl.getWidth();
30769 this.baseScale = height / this.imageEl.OriginHeight;
30771 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30772 width = this.thumbEl.getHeight();
30773 this.baseScale = width / this.imageEl.OriginWidth;
30780 width = this.thumbEl.getWidth();
30781 this.baseScale = width / this.imageEl.OriginWidth;
30783 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30784 height = this.thumbEl.getHeight();
30785 this.baseScale = height / this.imageEl.OriginHeight;
30788 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30790 height = this.thumbEl.getHeight();
30791 this.baseScale = height / this.imageEl.OriginHeight;
30793 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30794 width = this.thumbEl.getWidth();
30795 this.baseScale = width / this.imageEl.OriginWidth;
30803 getScaleLevel : function()
30805 return this.baseScale * Math.pow(1.1, this.scale);
30808 onTouchStart : function(e)
30810 if(!this.canvasLoaded){
30811 this.beforeSelectFile(e);
30815 var touches = e.browserEvent.touches;
30821 if(touches.length == 1){
30822 this.onMouseDown(e);
30826 if(touches.length != 2){
30832 for(var i = 0, finger; finger = touches[i]; i++){
30833 coords.push(finger.pageX, finger.pageY);
30836 var x = Math.pow(coords[0] - coords[2], 2);
30837 var y = Math.pow(coords[1] - coords[3], 2);
30839 this.startDistance = Math.sqrt(x + y);
30841 this.startScale = this.scale;
30843 this.pinching = true;
30844 this.dragable = false;
30848 onTouchMove : function(e)
30850 if(!this.pinching && !this.dragable){
30854 var touches = e.browserEvent.touches;
30861 this.onMouseMove(e);
30867 for(var i = 0, finger; finger = touches[i]; i++){
30868 coords.push(finger.pageX, finger.pageY);
30871 var x = Math.pow(coords[0] - coords[2], 2);
30872 var y = Math.pow(coords[1] - coords[3], 2);
30874 this.endDistance = Math.sqrt(x + y);
30876 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30878 if(!this.zoomable()){
30879 this.scale = this.startScale;
30887 onTouchEnd : function(e)
30889 this.pinching = false;
30890 this.dragable = false;
30894 process : function(file, crop)
30897 this.maskEl.mask(this.loadingText);
30900 this.xhr = new XMLHttpRequest();
30902 file.xhr = this.xhr;
30904 this.xhr.open(this.method, this.url, true);
30907 "Accept": "application/json",
30908 "Cache-Control": "no-cache",
30909 "X-Requested-With": "XMLHttpRequest"
30912 for (var headerName in headers) {
30913 var headerValue = headers[headerName];
30915 this.xhr.setRequestHeader(headerName, headerValue);
30921 this.xhr.onload = function()
30923 _this.xhrOnLoad(_this.xhr);
30926 this.xhr.onerror = function()
30928 _this.xhrOnError(_this.xhr);
30931 var formData = new FormData();
30933 formData.append('returnHTML', 'NO');
30936 formData.append('crop', crop);
30939 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30940 formData.append(this.paramName, file, file.name);
30943 if(typeof(file.filename) != 'undefined'){
30944 formData.append('filename', file.filename);
30947 if(typeof(file.mimetype) != 'undefined'){
30948 formData.append('mimetype', file.mimetype);
30951 if(this.fireEvent('arrange', this, formData) != false){
30952 this.xhr.send(formData);
30956 xhrOnLoad : function(xhr)
30959 this.maskEl.unmask();
30962 if (xhr.readyState !== 4) {
30963 this.fireEvent('exception', this, xhr);
30967 var response = Roo.decode(xhr.responseText);
30969 if(!response.success){
30970 this.fireEvent('exception', this, xhr);
30974 var response = Roo.decode(xhr.responseText);
30976 this.fireEvent('upload', this, response);
30980 xhrOnError : function()
30983 this.maskEl.unmask();
30986 Roo.log('xhr on error');
30988 var response = Roo.decode(xhr.responseText);
30994 prepare : function(file)
30997 this.maskEl.mask(this.loadingText);
31003 if(typeof(file) === 'string'){
31004 this.loadCanvas(file);
31008 if(!file || !this.urlAPI){
31013 this.cropType = file.type;
31017 if(this.fireEvent('prepare', this, this.file) != false){
31019 var reader = new FileReader();
31021 reader.onload = function (e) {
31022 if (e.target.error) {
31023 Roo.log(e.target.error);
31027 var buffer = e.target.result,
31028 dataView = new DataView(buffer),
31030 maxOffset = dataView.byteLength - 4,
31034 if (dataView.getUint16(0) === 0xffd8) {
31035 while (offset < maxOffset) {
31036 markerBytes = dataView.getUint16(offset);
31038 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31039 markerLength = dataView.getUint16(offset + 2) + 2;
31040 if (offset + markerLength > dataView.byteLength) {
31041 Roo.log('Invalid meta data: Invalid segment size.');
31045 if(markerBytes == 0xffe1){
31046 _this.parseExifData(
31053 offset += markerLength;
31063 var url = _this.urlAPI.createObjectURL(_this.file);
31065 _this.loadCanvas(url);
31070 reader.readAsArrayBuffer(this.file);
31076 parseExifData : function(dataView, offset, length)
31078 var tiffOffset = offset + 10,
31082 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31083 // No Exif data, might be XMP data instead
31087 // Check for the ASCII code for "Exif" (0x45786966):
31088 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31089 // No Exif data, might be XMP data instead
31092 if (tiffOffset + 8 > dataView.byteLength) {
31093 Roo.log('Invalid Exif data: Invalid segment size.');
31096 // Check for the two null bytes:
31097 if (dataView.getUint16(offset + 8) !== 0x0000) {
31098 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31101 // Check the byte alignment:
31102 switch (dataView.getUint16(tiffOffset)) {
31104 littleEndian = true;
31107 littleEndian = false;
31110 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31113 // Check for the TIFF tag marker (0x002A):
31114 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31115 Roo.log('Invalid Exif data: Missing TIFF marker.');
31118 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31119 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31121 this.parseExifTags(
31124 tiffOffset + dirOffset,
31129 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31134 if (dirOffset + 6 > dataView.byteLength) {
31135 Roo.log('Invalid Exif data: Invalid directory offset.');
31138 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31139 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31140 if (dirEndOffset + 4 > dataView.byteLength) {
31141 Roo.log('Invalid Exif data: Invalid directory size.');
31144 for (i = 0; i < tagsNumber; i += 1) {
31148 dirOffset + 2 + 12 * i, // tag offset
31152 // Return the offset to the next directory:
31153 return dataView.getUint32(dirEndOffset, littleEndian);
31156 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31158 var tag = dataView.getUint16(offset, littleEndian);
31160 this.exif[tag] = this.getExifValue(
31164 dataView.getUint16(offset + 2, littleEndian), // tag type
31165 dataView.getUint32(offset + 4, littleEndian), // tag length
31170 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31172 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31181 Roo.log('Invalid Exif data: Invalid tag type.');
31185 tagSize = tagType.size * length;
31186 // Determine if the value is contained in the dataOffset bytes,
31187 // or if the value at the dataOffset is a pointer to the actual data:
31188 dataOffset = tagSize > 4 ?
31189 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31190 if (dataOffset + tagSize > dataView.byteLength) {
31191 Roo.log('Invalid Exif data: Invalid data offset.');
31194 if (length === 1) {
31195 return tagType.getValue(dataView, dataOffset, littleEndian);
31198 for (i = 0; i < length; i += 1) {
31199 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31202 if (tagType.ascii) {
31204 // Concatenate the chars:
31205 for (i = 0; i < values.length; i += 1) {
31207 // Ignore the terminating NULL byte(s):
31208 if (c === '\u0000') {
31220 Roo.apply(Roo.bootstrap.UploadCropbox, {
31222 'Orientation': 0x0112
31226 1: 0, //'top-left',
31228 3: 180, //'bottom-right',
31229 // 4: 'bottom-left',
31231 6: 90, //'right-top',
31232 // 7: 'right-bottom',
31233 8: 270 //'left-bottom'
31237 // byte, 8-bit unsigned int:
31239 getValue: function (dataView, dataOffset) {
31240 return dataView.getUint8(dataOffset);
31244 // ascii, 8-bit byte:
31246 getValue: function (dataView, dataOffset) {
31247 return String.fromCharCode(dataView.getUint8(dataOffset));
31252 // short, 16 bit int:
31254 getValue: function (dataView, dataOffset, littleEndian) {
31255 return dataView.getUint16(dataOffset, littleEndian);
31259 // long, 32 bit int:
31261 getValue: function (dataView, dataOffset, littleEndian) {
31262 return dataView.getUint32(dataOffset, littleEndian);
31266 // rational = two long values, first is numerator, second is denominator:
31268 getValue: function (dataView, dataOffset, littleEndian) {
31269 return dataView.getUint32(dataOffset, littleEndian) /
31270 dataView.getUint32(dataOffset + 4, littleEndian);
31274 // slong, 32 bit signed int:
31276 getValue: function (dataView, dataOffset, littleEndian) {
31277 return dataView.getInt32(dataOffset, littleEndian);
31281 // srational, two slongs, first is numerator, second is denominator:
31283 getValue: function (dataView, dataOffset, littleEndian) {
31284 return dataView.getInt32(dataOffset, littleEndian) /
31285 dataView.getInt32(dataOffset + 4, littleEndian);
31295 cls : 'btn-group roo-upload-cropbox-rotate-left',
31296 action : 'rotate-left',
31300 cls : 'btn btn-default',
31301 html : '<i class="fa fa-undo"></i>'
31307 cls : 'btn-group roo-upload-cropbox-picture',
31308 action : 'picture',
31312 cls : 'btn btn-default',
31313 html : '<i class="fa fa-picture-o"></i>'
31319 cls : 'btn-group roo-upload-cropbox-rotate-right',
31320 action : 'rotate-right',
31324 cls : 'btn btn-default',
31325 html : '<i class="fa fa-repeat"></i>'
31333 cls : 'btn-group roo-upload-cropbox-rotate-left',
31334 action : 'rotate-left',
31338 cls : 'btn btn-default',
31339 html : '<i class="fa fa-undo"></i>'
31345 cls : 'btn-group roo-upload-cropbox-download',
31346 action : 'download',
31350 cls : 'btn btn-default',
31351 html : '<i class="fa fa-download"></i>'
31357 cls : 'btn-group roo-upload-cropbox-crop',
31362 cls : 'btn btn-default',
31363 html : '<i class="fa fa-crop"></i>'
31369 cls : 'btn-group roo-upload-cropbox-trash',
31374 cls : 'btn btn-default',
31375 html : '<i class="fa fa-trash"></i>'
31381 cls : 'btn-group roo-upload-cropbox-rotate-right',
31382 action : 'rotate-right',
31386 cls : 'btn btn-default',
31387 html : '<i class="fa fa-repeat"></i>'
31395 cls : 'btn-group roo-upload-cropbox-rotate-left',
31396 action : 'rotate-left',
31400 cls : 'btn btn-default',
31401 html : '<i class="fa fa-undo"></i>'
31407 cls : 'btn-group roo-upload-cropbox-rotate-right',
31408 action : 'rotate-right',
31412 cls : 'btn btn-default',
31413 html : '<i class="fa fa-repeat"></i>'
31426 * @class Roo.bootstrap.DocumentManager
31427 * @extends Roo.bootstrap.Component
31428 * Bootstrap DocumentManager class
31429 * @cfg {String} paramName default 'imageUpload'
31430 * @cfg {String} toolTipName default 'filename'
31431 * @cfg {String} method default POST
31432 * @cfg {String} url action url
31433 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31434 * @cfg {Boolean} multiple multiple upload default true
31435 * @cfg {Number} thumbSize default 300
31436 * @cfg {String} fieldLabel
31437 * @cfg {Number} labelWidth default 4
31438 * @cfg {String} labelAlign (left|top) default left
31439 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31440 * @cfg {Number} labellg set the width of label (1-12)
31441 * @cfg {Number} labelmd set the width of label (1-12)
31442 * @cfg {Number} labelsm set the width of label (1-12)
31443 * @cfg {Number} labelxs set the width of label (1-12)
31446 * Create a new DocumentManager
31447 * @param {Object} config The config object
31450 Roo.bootstrap.DocumentManager = function(config){
31451 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31454 this.delegates = [];
31459 * Fire when initial the DocumentManager
31460 * @param {Roo.bootstrap.DocumentManager} this
31465 * inspect selected file
31466 * @param {Roo.bootstrap.DocumentManager} this
31467 * @param {File} file
31472 * Fire when xhr load exception
31473 * @param {Roo.bootstrap.DocumentManager} this
31474 * @param {XMLHttpRequest} xhr
31476 "exception" : true,
31478 * @event afterupload
31479 * Fire when xhr load exception
31480 * @param {Roo.bootstrap.DocumentManager} this
31481 * @param {XMLHttpRequest} xhr
31483 "afterupload" : true,
31486 * prepare the form data
31487 * @param {Roo.bootstrap.DocumentManager} this
31488 * @param {Object} formData
31493 * Fire when remove the file
31494 * @param {Roo.bootstrap.DocumentManager} this
31495 * @param {Object} file
31500 * Fire after refresh the file
31501 * @param {Roo.bootstrap.DocumentManager} this
31506 * Fire after click the image
31507 * @param {Roo.bootstrap.DocumentManager} this
31508 * @param {Object} file
31513 * Fire when upload a image and editable set to true
31514 * @param {Roo.bootstrap.DocumentManager} this
31515 * @param {Object} file
31519 * @event beforeselectfile
31520 * Fire before select file
31521 * @param {Roo.bootstrap.DocumentManager} this
31523 "beforeselectfile" : true,
31526 * Fire before process file
31527 * @param {Roo.bootstrap.DocumentManager} this
31528 * @param {Object} file
31532 * @event previewrendered
31533 * Fire when preview rendered
31534 * @param {Roo.bootstrap.DocumentManager} this
31535 * @param {Object} file
31537 "previewrendered" : true,
31540 "previewResize" : true
31545 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31554 paramName : 'imageUpload',
31555 toolTipName : 'filename',
31558 labelAlign : 'left',
31568 getAutoCreate : function()
31570 var managerWidget = {
31572 cls : 'roo-document-manager',
31576 cls : 'roo-document-manager-selector',
31581 cls : 'roo-document-manager-uploader',
31585 cls : 'roo-document-manager-upload-btn',
31586 html : '<i class="fa fa-plus"></i>'
31597 cls : 'column col-md-12',
31602 if(this.fieldLabel.length){
31607 cls : 'column col-md-12',
31608 html : this.fieldLabel
31612 cls : 'column col-md-12',
31617 if(this.labelAlign == 'left'){
31622 html : this.fieldLabel
31631 if(this.labelWidth > 12){
31632 content[0].style = "width: " + this.labelWidth + 'px';
31635 if(this.labelWidth < 13 && this.labelmd == 0){
31636 this.labelmd = this.labelWidth;
31639 if(this.labellg > 0){
31640 content[0].cls += ' col-lg-' + this.labellg;
31641 content[1].cls += ' col-lg-' + (12 - this.labellg);
31644 if(this.labelmd > 0){
31645 content[0].cls += ' col-md-' + this.labelmd;
31646 content[1].cls += ' col-md-' + (12 - this.labelmd);
31649 if(this.labelsm > 0){
31650 content[0].cls += ' col-sm-' + this.labelsm;
31651 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31654 if(this.labelxs > 0){
31655 content[0].cls += ' col-xs-' + this.labelxs;
31656 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31664 cls : 'row clearfix',
31672 initEvents : function()
31674 this.managerEl = this.el.select('.roo-document-manager', true).first();
31675 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31677 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31678 this.selectorEl.hide();
31681 this.selectorEl.attr('multiple', 'multiple');
31684 this.selectorEl.on('change', this.onFileSelected, this);
31686 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31687 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31689 this.uploader.on('click', this.onUploaderClick, this);
31691 this.renderProgressDialog();
31695 window.addEventListener("resize", function() { _this.refresh(); } );
31697 this.fireEvent('initial', this);
31700 renderProgressDialog : function()
31704 this.progressDialog = new Roo.bootstrap.Modal({
31705 cls : 'roo-document-manager-progress-dialog',
31706 allow_close : false,
31717 btnclick : function() {
31718 _this.uploadCancel();
31724 this.progressDialog.render(Roo.get(document.body));
31726 this.progress = new Roo.bootstrap.Progress({
31727 cls : 'roo-document-manager-progress',
31732 this.progress.render(this.progressDialog.getChildContainer());
31734 this.progressBar = new Roo.bootstrap.ProgressBar({
31735 cls : 'roo-document-manager-progress-bar',
31738 aria_valuemax : 12,
31742 this.progressBar.render(this.progress.getChildContainer());
31745 onUploaderClick : function(e)
31747 e.preventDefault();
31749 if(this.fireEvent('beforeselectfile', this) != false){
31750 this.selectorEl.dom.click();
31755 onFileSelected : function(e)
31757 e.preventDefault();
31759 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31763 Roo.each(this.selectorEl.dom.files, function(file){
31764 if(this.fireEvent('inspect', this, file) != false){
31765 this.files.push(file);
31775 this.selectorEl.dom.value = '';
31777 if(!this.files || !this.files.length){
31781 if(this.boxes > 0 && this.files.length > this.boxes){
31782 this.files = this.files.slice(0, this.boxes);
31785 this.uploader.show();
31787 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31788 this.uploader.hide();
31797 Roo.each(this.files, function(file){
31799 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31800 var f = this.renderPreview(file);
31805 if(file.type.indexOf('image') != -1){
31806 this.delegates.push(
31808 _this.process(file);
31809 }).createDelegate(this)
31817 _this.process(file);
31818 }).createDelegate(this)
31823 this.files = files;
31825 this.delegates = this.delegates.concat(docs);
31827 if(!this.delegates.length){
31832 this.progressBar.aria_valuemax = this.delegates.length;
31839 arrange : function()
31841 if(!this.delegates.length){
31842 this.progressDialog.hide();
31847 var delegate = this.delegates.shift();
31849 this.progressDialog.show();
31851 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31853 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31858 refresh : function()
31860 this.uploader.show();
31862 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31863 this.uploader.hide();
31866 Roo.isTouch ? this.closable(false) : this.closable(true);
31868 this.fireEvent('refresh', this);
31871 onRemove : function(e, el, o)
31873 e.preventDefault();
31875 this.fireEvent('remove', this, o);
31879 remove : function(o)
31883 Roo.each(this.files, function(file){
31884 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31893 this.files = files;
31900 Roo.each(this.files, function(file){
31905 file.target.remove();
31914 onClick : function(e, el, o)
31916 e.preventDefault();
31918 this.fireEvent('click', this, o);
31922 closable : function(closable)
31924 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31926 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31938 xhrOnLoad : function(xhr)
31940 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31944 if (xhr.readyState !== 4) {
31946 this.fireEvent('exception', this, xhr);
31950 var response = Roo.decode(xhr.responseText);
31952 if(!response.success){
31954 this.fireEvent('exception', this, xhr);
31958 var file = this.renderPreview(response.data);
31960 this.files.push(file);
31964 this.fireEvent('afterupload', this, xhr);
31968 xhrOnError : function(xhr)
31970 Roo.log('xhr on error');
31972 var response = Roo.decode(xhr.responseText);
31979 process : function(file)
31981 if(this.fireEvent('process', this, file) !== false){
31982 if(this.editable && file.type.indexOf('image') != -1){
31983 this.fireEvent('edit', this, file);
31987 this.uploadStart(file, false);
31994 uploadStart : function(file, crop)
31996 this.xhr = new XMLHttpRequest();
31998 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32003 file.xhr = this.xhr;
32005 this.managerEl.createChild({
32007 cls : 'roo-document-manager-loading',
32011 tooltip : file.name,
32012 cls : 'roo-document-manager-thumb',
32013 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32019 this.xhr.open(this.method, this.url, true);
32022 "Accept": "application/json",
32023 "Cache-Control": "no-cache",
32024 "X-Requested-With": "XMLHttpRequest"
32027 for (var headerName in headers) {
32028 var headerValue = headers[headerName];
32030 this.xhr.setRequestHeader(headerName, headerValue);
32036 this.xhr.onload = function()
32038 _this.xhrOnLoad(_this.xhr);
32041 this.xhr.onerror = function()
32043 _this.xhrOnError(_this.xhr);
32046 var formData = new FormData();
32048 formData.append('returnHTML', 'NO');
32051 formData.append('crop', crop);
32054 formData.append(this.paramName, file, file.name);
32061 if(this.fireEvent('prepare', this, formData, options) != false){
32063 if(options.manually){
32067 this.xhr.send(formData);
32071 this.uploadCancel();
32074 uploadCancel : function()
32080 this.delegates = [];
32082 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32089 renderPreview : function(file)
32091 if(typeof(file.target) != 'undefined' && file.target){
32095 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32097 var previewEl = this.managerEl.createChild({
32099 cls : 'roo-document-manager-preview',
32103 tooltip : file[this.toolTipName],
32104 cls : 'roo-document-manager-thumb',
32105 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32110 html : '<i class="fa fa-times-circle"></i>'
32115 var close = previewEl.select('button.close', true).first();
32117 close.on('click', this.onRemove, this, file);
32119 file.target = previewEl;
32121 var image = previewEl.select('img', true).first();
32125 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32127 image.on('click', this.onClick, this, file);
32129 this.fireEvent('previewrendered', this, file);
32135 onPreviewLoad : function(file, image)
32137 if(typeof(file.target) == 'undefined' || !file.target){
32141 var width = image.dom.naturalWidth || image.dom.width;
32142 var height = image.dom.naturalHeight || image.dom.height;
32144 if(!this.previewResize) {
32148 if(width > height){
32149 file.target.addClass('wide');
32153 file.target.addClass('tall');
32158 uploadFromSource : function(file, crop)
32160 this.xhr = new XMLHttpRequest();
32162 this.managerEl.createChild({
32164 cls : 'roo-document-manager-loading',
32168 tooltip : file.name,
32169 cls : 'roo-document-manager-thumb',
32170 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32176 this.xhr.open(this.method, this.url, true);
32179 "Accept": "application/json",
32180 "Cache-Control": "no-cache",
32181 "X-Requested-With": "XMLHttpRequest"
32184 for (var headerName in headers) {
32185 var headerValue = headers[headerName];
32187 this.xhr.setRequestHeader(headerName, headerValue);
32193 this.xhr.onload = function()
32195 _this.xhrOnLoad(_this.xhr);
32198 this.xhr.onerror = function()
32200 _this.xhrOnError(_this.xhr);
32203 var formData = new FormData();
32205 formData.append('returnHTML', 'NO');
32207 formData.append('crop', crop);
32209 if(typeof(file.filename) != 'undefined'){
32210 formData.append('filename', file.filename);
32213 if(typeof(file.mimetype) != 'undefined'){
32214 formData.append('mimetype', file.mimetype);
32219 if(this.fireEvent('prepare', this, formData) != false){
32220 this.xhr.send(formData);
32230 * @class Roo.bootstrap.DocumentViewer
32231 * @extends Roo.bootstrap.Component
32232 * Bootstrap DocumentViewer class
32233 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32234 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32237 * Create a new DocumentViewer
32238 * @param {Object} config The config object
32241 Roo.bootstrap.DocumentViewer = function(config){
32242 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32247 * Fire after initEvent
32248 * @param {Roo.bootstrap.DocumentViewer} this
32254 * @param {Roo.bootstrap.DocumentViewer} this
32259 * Fire after download button
32260 * @param {Roo.bootstrap.DocumentViewer} this
32265 * Fire after trash button
32266 * @param {Roo.bootstrap.DocumentViewer} this
32273 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32275 showDownload : true,
32279 getAutoCreate : function()
32283 cls : 'roo-document-viewer',
32287 cls : 'roo-document-viewer-body',
32291 cls : 'roo-document-viewer-thumb',
32295 cls : 'roo-document-viewer-image'
32303 cls : 'roo-document-viewer-footer',
32306 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32310 cls : 'btn-group roo-document-viewer-download',
32314 cls : 'btn btn-default',
32315 html : '<i class="fa fa-download"></i>'
32321 cls : 'btn-group roo-document-viewer-trash',
32325 cls : 'btn btn-default',
32326 html : '<i class="fa fa-trash"></i>'
32339 initEvents : function()
32341 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32342 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32344 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32345 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32347 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32348 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32350 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32351 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32353 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32354 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32356 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32357 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32359 this.bodyEl.on('click', this.onClick, this);
32360 this.downloadBtn.on('click', this.onDownload, this);
32361 this.trashBtn.on('click', this.onTrash, this);
32363 this.downloadBtn.hide();
32364 this.trashBtn.hide();
32366 if(this.showDownload){
32367 this.downloadBtn.show();
32370 if(this.showTrash){
32371 this.trashBtn.show();
32374 if(!this.showDownload && !this.showTrash) {
32375 this.footerEl.hide();
32380 initial : function()
32382 this.fireEvent('initial', this);
32386 onClick : function(e)
32388 e.preventDefault();
32390 this.fireEvent('click', this);
32393 onDownload : function(e)
32395 e.preventDefault();
32397 this.fireEvent('download', this);
32400 onTrash : function(e)
32402 e.preventDefault();
32404 this.fireEvent('trash', this);
32416 * @class Roo.bootstrap.NavProgressBar
32417 * @extends Roo.bootstrap.Component
32418 * Bootstrap NavProgressBar class
32421 * Create a new nav progress bar
32422 * @param {Object} config The config object
32425 Roo.bootstrap.NavProgressBar = function(config){
32426 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32428 this.bullets = this.bullets || [];
32430 // Roo.bootstrap.NavProgressBar.register(this);
32434 * Fires when the active item changes
32435 * @param {Roo.bootstrap.NavProgressBar} this
32436 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32437 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32444 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32449 getAutoCreate : function()
32451 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32455 cls : 'roo-navigation-bar-group',
32459 cls : 'roo-navigation-top-bar'
32463 cls : 'roo-navigation-bullets-bar',
32467 cls : 'roo-navigation-bar'
32474 cls : 'roo-navigation-bottom-bar'
32484 initEvents: function()
32489 onRender : function(ct, position)
32491 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32493 if(this.bullets.length){
32494 Roo.each(this.bullets, function(b){
32503 addItem : function(cfg)
32505 var item = new Roo.bootstrap.NavProgressItem(cfg);
32507 item.parentId = this.id;
32508 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32511 var top = new Roo.bootstrap.Element({
32513 cls : 'roo-navigation-bar-text'
32516 var bottom = new Roo.bootstrap.Element({
32518 cls : 'roo-navigation-bar-text'
32521 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32522 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32524 var topText = new Roo.bootstrap.Element({
32526 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32529 var bottomText = new Roo.bootstrap.Element({
32531 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32534 topText.onRender(top.el, null);
32535 bottomText.onRender(bottom.el, null);
32538 item.bottomEl = bottom;
32541 this.barItems.push(item);
32546 getActive : function()
32548 var active = false;
32550 Roo.each(this.barItems, function(v){
32552 if (!v.isActive()) {
32564 setActiveItem : function(item)
32568 Roo.each(this.barItems, function(v){
32569 if (v.rid == item.rid) {
32573 if (v.isActive()) {
32574 v.setActive(false);
32579 item.setActive(true);
32581 this.fireEvent('changed', this, item, prev);
32584 getBarItem: function(rid)
32588 Roo.each(this.barItems, function(e) {
32589 if (e.rid != rid) {
32600 indexOfItem : function(item)
32604 Roo.each(this.barItems, function(v, i){
32606 if (v.rid != item.rid) {
32617 setActiveNext : function()
32619 var i = this.indexOfItem(this.getActive());
32621 if (i > this.barItems.length) {
32625 this.setActiveItem(this.barItems[i+1]);
32628 setActivePrev : function()
32630 var i = this.indexOfItem(this.getActive());
32636 this.setActiveItem(this.barItems[i-1]);
32639 format : function()
32641 if(!this.barItems.length){
32645 var width = 100 / this.barItems.length;
32647 Roo.each(this.barItems, function(i){
32648 i.el.setStyle('width', width + '%');
32649 i.topEl.el.setStyle('width', width + '%');
32650 i.bottomEl.el.setStyle('width', width + '%');
32659 * Nav Progress Item
32664 * @class Roo.bootstrap.NavProgressItem
32665 * @extends Roo.bootstrap.Component
32666 * Bootstrap NavProgressItem class
32667 * @cfg {String} rid the reference id
32668 * @cfg {Boolean} active (true|false) Is item active default false
32669 * @cfg {Boolean} disabled (true|false) Is item active default false
32670 * @cfg {String} html
32671 * @cfg {String} position (top|bottom) text position default bottom
32672 * @cfg {String} icon show icon instead of number
32675 * Create a new NavProgressItem
32676 * @param {Object} config The config object
32678 Roo.bootstrap.NavProgressItem = function(config){
32679 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32684 * The raw click event for the entire grid.
32685 * @param {Roo.bootstrap.NavProgressItem} this
32686 * @param {Roo.EventObject} e
32693 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32699 position : 'bottom',
32702 getAutoCreate : function()
32704 var iconCls = 'roo-navigation-bar-item-icon';
32706 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32710 cls: 'roo-navigation-bar-item',
32720 cfg.cls += ' active';
32723 cfg.cls += ' disabled';
32729 disable : function()
32731 this.setDisabled(true);
32734 enable : function()
32736 this.setDisabled(false);
32739 initEvents: function()
32741 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32743 this.iconEl.on('click', this.onClick, this);
32746 onClick : function(e)
32748 e.preventDefault();
32754 if(this.fireEvent('click', this, e) === false){
32758 this.parent().setActiveItem(this);
32761 isActive: function ()
32763 return this.active;
32766 setActive : function(state)
32768 if(this.active == state){
32772 this.active = state;
32775 this.el.addClass('active');
32779 this.el.removeClass('active');
32784 setDisabled : function(state)
32786 if(this.disabled == state){
32790 this.disabled = state;
32793 this.el.addClass('disabled');
32797 this.el.removeClass('disabled');
32800 tooltipEl : function()
32802 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32815 * @class Roo.bootstrap.FieldLabel
32816 * @extends Roo.bootstrap.Component
32817 * Bootstrap FieldLabel class
32818 * @cfg {String} html contents of the element
32819 * @cfg {String} tag tag of the element default label
32820 * @cfg {String} cls class of the element
32821 * @cfg {String} target label target
32822 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32823 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32824 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32825 * @cfg {String} iconTooltip default "This field is required"
32826 * @cfg {String} indicatorpos (left|right) default left
32829 * Create a new FieldLabel
32830 * @param {Object} config The config object
32833 Roo.bootstrap.FieldLabel = function(config){
32834 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32839 * Fires after the field has been marked as invalid.
32840 * @param {Roo.form.FieldLabel} this
32841 * @param {String} msg The validation message
32846 * Fires after the field has been validated with no errors.
32847 * @param {Roo.form.FieldLabel} this
32853 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32860 invalidClass : 'has-warning',
32861 validClass : 'has-success',
32862 iconTooltip : 'This field is required',
32863 indicatorpos : 'left',
32865 getAutoCreate : function(){
32868 if (!this.allowBlank) {
32874 cls : 'roo-bootstrap-field-label ' + this.cls,
32879 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32880 tooltip : this.iconTooltip
32889 if(this.indicatorpos == 'right'){
32892 cls : 'roo-bootstrap-field-label ' + this.cls,
32901 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32902 tooltip : this.iconTooltip
32911 initEvents: function()
32913 Roo.bootstrap.Element.superclass.initEvents.call(this);
32915 this.indicator = this.indicatorEl();
32917 if(this.indicator){
32918 this.indicator.removeClass('visible');
32919 this.indicator.addClass('invisible');
32922 Roo.bootstrap.FieldLabel.register(this);
32925 indicatorEl : function()
32927 var indicator = this.el.select('i.roo-required-indicator',true).first();
32938 * Mark this field as valid
32940 markValid : function()
32942 if(this.indicator){
32943 this.indicator.removeClass('visible');
32944 this.indicator.addClass('invisible');
32946 if (Roo.bootstrap.version == 3) {
32947 this.el.removeClass(this.invalidClass);
32948 this.el.addClass(this.validClass);
32950 this.el.removeClass('is-invalid');
32951 this.el.addClass('is-valid');
32955 this.fireEvent('valid', this);
32959 * Mark this field as invalid
32960 * @param {String} msg The validation message
32962 markInvalid : function(msg)
32964 if(this.indicator){
32965 this.indicator.removeClass('invisible');
32966 this.indicator.addClass('visible');
32968 if (Roo.bootstrap.version == 3) {
32969 this.el.removeClass(this.validClass);
32970 this.el.addClass(this.invalidClass);
32972 this.el.removeClass('is-valid');
32973 this.el.addClass('is-invalid');
32977 this.fireEvent('invalid', this, msg);
32983 Roo.apply(Roo.bootstrap.FieldLabel, {
32988 * register a FieldLabel Group
32989 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32991 register : function(label)
32993 if(this.groups.hasOwnProperty(label.target)){
32997 this.groups[label.target] = label;
33001 * fetch a FieldLabel Group based on the target
33002 * @param {string} target
33003 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33005 get: function(target) {
33006 if (typeof(this.groups[target]) == 'undefined') {
33010 return this.groups[target] ;
33019 * page DateSplitField.
33025 * @class Roo.bootstrap.DateSplitField
33026 * @extends Roo.bootstrap.Component
33027 * Bootstrap DateSplitField class
33028 * @cfg {string} fieldLabel - the label associated
33029 * @cfg {Number} labelWidth set the width of label (0-12)
33030 * @cfg {String} labelAlign (top|left)
33031 * @cfg {Boolean} dayAllowBlank (true|false) default false
33032 * @cfg {Boolean} monthAllowBlank (true|false) default false
33033 * @cfg {Boolean} yearAllowBlank (true|false) default false
33034 * @cfg {string} dayPlaceholder
33035 * @cfg {string} monthPlaceholder
33036 * @cfg {string} yearPlaceholder
33037 * @cfg {string} dayFormat default 'd'
33038 * @cfg {string} monthFormat default 'm'
33039 * @cfg {string} yearFormat default 'Y'
33040 * @cfg {Number} labellg set the width of label (1-12)
33041 * @cfg {Number} labelmd set the width of label (1-12)
33042 * @cfg {Number} labelsm set the width of label (1-12)
33043 * @cfg {Number} labelxs set the width of label (1-12)
33047 * Create a new DateSplitField
33048 * @param {Object} config The config object
33051 Roo.bootstrap.DateSplitField = function(config){
33052 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33058 * getting the data of years
33059 * @param {Roo.bootstrap.DateSplitField} this
33060 * @param {Object} years
33065 * getting the data of days
33066 * @param {Roo.bootstrap.DateSplitField} this
33067 * @param {Object} days
33072 * Fires after the field has been marked as invalid.
33073 * @param {Roo.form.Field} this
33074 * @param {String} msg The validation message
33079 * Fires after the field has been validated with no errors.
33080 * @param {Roo.form.Field} this
33086 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33089 labelAlign : 'top',
33091 dayAllowBlank : false,
33092 monthAllowBlank : false,
33093 yearAllowBlank : false,
33094 dayPlaceholder : '',
33095 monthPlaceholder : '',
33096 yearPlaceholder : '',
33100 isFormField : true,
33106 getAutoCreate : function()
33110 cls : 'row roo-date-split-field-group',
33115 cls : 'form-hidden-field roo-date-split-field-group-value',
33121 var labelCls = 'col-md-12';
33122 var contentCls = 'col-md-4';
33124 if(this.fieldLabel){
33128 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33132 html : this.fieldLabel
33137 if(this.labelAlign == 'left'){
33139 if(this.labelWidth > 12){
33140 label.style = "width: " + this.labelWidth + 'px';
33143 if(this.labelWidth < 13 && this.labelmd == 0){
33144 this.labelmd = this.labelWidth;
33147 if(this.labellg > 0){
33148 labelCls = ' col-lg-' + this.labellg;
33149 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33152 if(this.labelmd > 0){
33153 labelCls = ' col-md-' + this.labelmd;
33154 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33157 if(this.labelsm > 0){
33158 labelCls = ' col-sm-' + this.labelsm;
33159 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33162 if(this.labelxs > 0){
33163 labelCls = ' col-xs-' + this.labelxs;
33164 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33168 label.cls += ' ' + labelCls;
33170 cfg.cn.push(label);
33173 Roo.each(['day', 'month', 'year'], function(t){
33176 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33183 inputEl: function ()
33185 return this.el.select('.roo-date-split-field-group-value', true).first();
33188 onRender : function(ct, position)
33192 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33194 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33196 this.dayField = new Roo.bootstrap.ComboBox({
33197 allowBlank : this.dayAllowBlank,
33198 alwaysQuery : true,
33199 displayField : 'value',
33202 forceSelection : true,
33204 placeholder : this.dayPlaceholder,
33205 selectOnFocus : true,
33206 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33207 triggerAction : 'all',
33209 valueField : 'value',
33210 store : new Roo.data.SimpleStore({
33211 data : (function() {
33213 _this.fireEvent('days', _this, days);
33216 fields : [ 'value' ]
33219 select : function (_self, record, index)
33221 _this.setValue(_this.getValue());
33226 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33228 this.monthField = new Roo.bootstrap.MonthField({
33229 after : '<i class=\"fa fa-calendar\"></i>',
33230 allowBlank : this.monthAllowBlank,
33231 placeholder : this.monthPlaceholder,
33234 render : function (_self)
33236 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33237 e.preventDefault();
33241 select : function (_self, oldvalue, newvalue)
33243 _this.setValue(_this.getValue());
33248 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33250 this.yearField = new Roo.bootstrap.ComboBox({
33251 allowBlank : this.yearAllowBlank,
33252 alwaysQuery : true,
33253 displayField : 'value',
33256 forceSelection : true,
33258 placeholder : this.yearPlaceholder,
33259 selectOnFocus : true,
33260 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33261 triggerAction : 'all',
33263 valueField : 'value',
33264 store : new Roo.data.SimpleStore({
33265 data : (function() {
33267 _this.fireEvent('years', _this, years);
33270 fields : [ 'value' ]
33273 select : function (_self, record, index)
33275 _this.setValue(_this.getValue());
33280 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33283 setValue : function(v, format)
33285 this.inputEl.dom.value = v;
33287 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33289 var d = Date.parseDate(v, f);
33296 this.setDay(d.format(this.dayFormat));
33297 this.setMonth(d.format(this.monthFormat));
33298 this.setYear(d.format(this.yearFormat));
33305 setDay : function(v)
33307 this.dayField.setValue(v);
33308 this.inputEl.dom.value = this.getValue();
33313 setMonth : function(v)
33315 this.monthField.setValue(v, true);
33316 this.inputEl.dom.value = this.getValue();
33321 setYear : function(v)
33323 this.yearField.setValue(v);
33324 this.inputEl.dom.value = this.getValue();
33329 getDay : function()
33331 return this.dayField.getValue();
33334 getMonth : function()
33336 return this.monthField.getValue();
33339 getYear : function()
33341 return this.yearField.getValue();
33344 getValue : function()
33346 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33348 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33358 this.inputEl.dom.value = '';
33363 validate : function()
33365 var d = this.dayField.validate();
33366 var m = this.monthField.validate();
33367 var y = this.yearField.validate();
33372 (!this.dayAllowBlank && !d) ||
33373 (!this.monthAllowBlank && !m) ||
33374 (!this.yearAllowBlank && !y)
33379 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33388 this.markInvalid();
33393 markValid : function()
33396 var label = this.el.select('label', true).first();
33397 var icon = this.el.select('i.fa-star', true).first();
33403 this.fireEvent('valid', this);
33407 * Mark this field as invalid
33408 * @param {String} msg The validation message
33410 markInvalid : function(msg)
33413 var label = this.el.select('label', true).first();
33414 var icon = this.el.select('i.fa-star', true).first();
33416 if(label && !icon){
33417 this.el.select('.roo-date-split-field-label', true).createChild({
33419 cls : 'text-danger fa fa-lg fa-star',
33420 tooltip : 'This field is required',
33421 style : 'margin-right:5px;'
33425 this.fireEvent('invalid', this, msg);
33428 clearInvalid : function()
33430 var label = this.el.select('label', true).first();
33431 var icon = this.el.select('i.fa-star', true).first();
33437 this.fireEvent('valid', this);
33440 getName: function()
33450 * http://masonry.desandro.com
33452 * The idea is to render all the bricks based on vertical width...
33454 * The original code extends 'outlayer' - we might need to use that....
33460 * @class Roo.bootstrap.LayoutMasonry
33461 * @extends Roo.bootstrap.Component
33462 * Bootstrap Layout Masonry class
33465 * Create a new Element
33466 * @param {Object} config The config object
33469 Roo.bootstrap.LayoutMasonry = function(config){
33471 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33475 Roo.bootstrap.LayoutMasonry.register(this);
33481 * Fire after layout the items
33482 * @param {Roo.bootstrap.LayoutMasonry} this
33483 * @param {Roo.EventObject} e
33490 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33493 * @cfg {Boolean} isLayoutInstant = no animation?
33495 isLayoutInstant : false, // needed?
33498 * @cfg {Number} boxWidth width of the columns
33503 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33508 * @cfg {Number} padWidth padding below box..
33513 * @cfg {Number} gutter gutter width..
33518 * @cfg {Number} maxCols maximum number of columns
33524 * @cfg {Boolean} isAutoInitial defalut true
33526 isAutoInitial : true,
33531 * @cfg {Boolean} isHorizontal defalut false
33533 isHorizontal : false,
33535 currentSize : null,
33541 bricks: null, //CompositeElement
33545 _isLayoutInited : false,
33547 // isAlternative : false, // only use for vertical layout...
33550 * @cfg {Number} alternativePadWidth padding below box..
33552 alternativePadWidth : 50,
33554 selectedBrick : [],
33556 getAutoCreate : function(){
33558 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33562 cls: 'blog-masonary-wrapper ' + this.cls,
33564 cls : 'mas-boxes masonary'
33571 getChildContainer: function( )
33573 if (this.boxesEl) {
33574 return this.boxesEl;
33577 this.boxesEl = this.el.select('.mas-boxes').first();
33579 return this.boxesEl;
33583 initEvents : function()
33587 if(this.isAutoInitial){
33588 Roo.log('hook children rendered');
33589 this.on('childrenrendered', function() {
33590 Roo.log('children rendered');
33596 initial : function()
33598 this.selectedBrick = [];
33600 this.currentSize = this.el.getBox(true);
33602 Roo.EventManager.onWindowResize(this.resize, this);
33604 if(!this.isAutoInitial){
33612 //this.layout.defer(500,this);
33616 resize : function()
33618 var cs = this.el.getBox(true);
33621 this.currentSize.width == cs.width &&
33622 this.currentSize.x == cs.x &&
33623 this.currentSize.height == cs.height &&
33624 this.currentSize.y == cs.y
33626 Roo.log("no change in with or X or Y");
33630 this.currentSize = cs;
33636 layout : function()
33638 this._resetLayout();
33640 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33642 this.layoutItems( isInstant );
33644 this._isLayoutInited = true;
33646 this.fireEvent('layout', this);
33650 _resetLayout : function()
33652 if(this.isHorizontal){
33653 this.horizontalMeasureColumns();
33657 this.verticalMeasureColumns();
33661 verticalMeasureColumns : function()
33663 this.getContainerWidth();
33665 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33666 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33670 var boxWidth = this.boxWidth + this.padWidth;
33672 if(this.containerWidth < this.boxWidth){
33673 boxWidth = this.containerWidth
33676 var containerWidth = this.containerWidth;
33678 var cols = Math.floor(containerWidth / boxWidth);
33680 this.cols = Math.max( cols, 1 );
33682 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33684 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33686 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33688 this.colWidth = boxWidth + avail - this.padWidth;
33690 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33691 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33694 horizontalMeasureColumns : function()
33696 this.getContainerWidth();
33698 var boxWidth = this.boxWidth;
33700 if(this.containerWidth < boxWidth){
33701 boxWidth = this.containerWidth;
33704 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33706 this.el.setHeight(boxWidth);
33710 getContainerWidth : function()
33712 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33715 layoutItems : function( isInstant )
33717 Roo.log(this.bricks);
33719 var items = Roo.apply([], this.bricks);
33721 if(this.isHorizontal){
33722 this._horizontalLayoutItems( items , isInstant );
33726 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33727 // this._verticalAlternativeLayoutItems( items , isInstant );
33731 this._verticalLayoutItems( items , isInstant );
33735 _verticalLayoutItems : function ( items , isInstant)
33737 if ( !items || !items.length ) {
33742 ['xs', 'xs', 'xs', 'tall'],
33743 ['xs', 'xs', 'tall'],
33744 ['xs', 'xs', 'sm'],
33745 ['xs', 'xs', 'xs'],
33751 ['sm', 'xs', 'xs'],
33755 ['tall', 'xs', 'xs', 'xs'],
33756 ['tall', 'xs', 'xs'],
33768 Roo.each(items, function(item, k){
33770 switch (item.size) {
33771 // these layouts take up a full box,
33782 boxes.push([item]);
33805 var filterPattern = function(box, length)
33813 var pattern = box.slice(0, length);
33817 Roo.each(pattern, function(i){
33818 format.push(i.size);
33821 Roo.each(standard, function(s){
33823 if(String(s) != String(format)){
33832 if(!match && length == 1){
33837 filterPattern(box, length - 1);
33841 queue.push(pattern);
33843 box = box.slice(length, box.length);
33845 filterPattern(box, 4);
33851 Roo.each(boxes, function(box, k){
33857 if(box.length == 1){
33862 filterPattern(box, 4);
33866 this._processVerticalLayoutQueue( queue, isInstant );
33870 // _verticalAlternativeLayoutItems : function( items , isInstant )
33872 // if ( !items || !items.length ) {
33876 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33880 _horizontalLayoutItems : function ( items , isInstant)
33882 if ( !items || !items.length || items.length < 3) {
33888 var eItems = items.slice(0, 3);
33890 items = items.slice(3, items.length);
33893 ['xs', 'xs', 'xs', 'wide'],
33894 ['xs', 'xs', 'wide'],
33895 ['xs', 'xs', 'sm'],
33896 ['xs', 'xs', 'xs'],
33902 ['sm', 'xs', 'xs'],
33906 ['wide', 'xs', 'xs', 'xs'],
33907 ['wide', 'xs', 'xs'],
33920 Roo.each(items, function(item, k){
33922 switch (item.size) {
33933 boxes.push([item]);
33957 var filterPattern = function(box, length)
33965 var pattern = box.slice(0, length);
33969 Roo.each(pattern, function(i){
33970 format.push(i.size);
33973 Roo.each(standard, function(s){
33975 if(String(s) != String(format)){
33984 if(!match && length == 1){
33989 filterPattern(box, length - 1);
33993 queue.push(pattern);
33995 box = box.slice(length, box.length);
33997 filterPattern(box, 4);
34003 Roo.each(boxes, function(box, k){
34009 if(box.length == 1){
34014 filterPattern(box, 4);
34021 var pos = this.el.getBox(true);
34025 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34027 var hit_end = false;
34029 Roo.each(queue, function(box){
34033 Roo.each(box, function(b){
34035 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34045 Roo.each(box, function(b){
34047 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34050 mx = Math.max(mx, b.x);
34054 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34058 Roo.each(box, function(b){
34060 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34074 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34077 /** Sets position of item in DOM
34078 * @param {Element} item
34079 * @param {Number} x - horizontal position
34080 * @param {Number} y - vertical position
34081 * @param {Boolean} isInstant - disables transitions
34083 _processVerticalLayoutQueue : function( queue, isInstant )
34085 var pos = this.el.getBox(true);
34090 for (var i = 0; i < this.cols; i++){
34094 Roo.each(queue, function(box, k){
34096 var col = k % this.cols;
34098 Roo.each(box, function(b,kk){
34100 b.el.position('absolute');
34102 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34103 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34105 if(b.size == 'md-left' || b.size == 'md-right'){
34106 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34107 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34110 b.el.setWidth(width);
34111 b.el.setHeight(height);
34113 b.el.select('iframe',true).setSize(width,height);
34117 for (var i = 0; i < this.cols; i++){
34119 if(maxY[i] < maxY[col]){
34124 col = Math.min(col, i);
34128 x = pos.x + col * (this.colWidth + this.padWidth);
34132 var positions = [];
34134 switch (box.length){
34136 positions = this.getVerticalOneBoxColPositions(x, y, box);
34139 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34142 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34145 positions = this.getVerticalFourBoxColPositions(x, y, box);
34151 Roo.each(box, function(b,kk){
34153 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34155 var sz = b.el.getSize();
34157 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34165 for (var i = 0; i < this.cols; i++){
34166 mY = Math.max(mY, maxY[i]);
34169 this.el.setHeight(mY - pos.y);
34173 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34175 // var pos = this.el.getBox(true);
34178 // var maxX = pos.right;
34180 // var maxHeight = 0;
34182 // Roo.each(items, function(item, k){
34186 // item.el.position('absolute');
34188 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34190 // item.el.setWidth(width);
34192 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34194 // item.el.setHeight(height);
34197 // item.el.setXY([x, y], isInstant ? false : true);
34199 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34202 // y = y + height + this.alternativePadWidth;
34204 // maxHeight = maxHeight + height + this.alternativePadWidth;
34208 // this.el.setHeight(maxHeight);
34212 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34214 var pos = this.el.getBox(true);
34219 var maxX = pos.right;
34221 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34223 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34225 Roo.each(queue, function(box, k){
34227 Roo.each(box, function(b, kk){
34229 b.el.position('absolute');
34231 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34232 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34234 if(b.size == 'md-left' || b.size == 'md-right'){
34235 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34236 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34239 b.el.setWidth(width);
34240 b.el.setHeight(height);
34248 var positions = [];
34250 switch (box.length){
34252 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34255 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34258 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34261 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34267 Roo.each(box, function(b,kk){
34269 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34271 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34279 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34281 Roo.each(eItems, function(b,k){
34283 b.size = (k == 0) ? 'sm' : 'xs';
34284 b.x = (k == 0) ? 2 : 1;
34285 b.y = (k == 0) ? 2 : 1;
34287 b.el.position('absolute');
34289 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34291 b.el.setWidth(width);
34293 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34295 b.el.setHeight(height);
34299 var positions = [];
34302 x : maxX - this.unitWidth * 2 - this.gutter,
34307 x : maxX - this.unitWidth,
34308 y : minY + (this.unitWidth + this.gutter) * 2
34312 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34316 Roo.each(eItems, function(b,k){
34318 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34324 getVerticalOneBoxColPositions : function(x, y, box)
34328 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34330 if(box[0].size == 'md-left'){
34334 if(box[0].size == 'md-right'){
34339 x : x + (this.unitWidth + this.gutter) * rand,
34346 getVerticalTwoBoxColPositions : function(x, y, box)
34350 if(box[0].size == 'xs'){
34354 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34358 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34372 x : x + (this.unitWidth + this.gutter) * 2,
34373 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34380 getVerticalThreeBoxColPositions : function(x, y, box)
34384 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34392 x : x + (this.unitWidth + this.gutter) * 1,
34397 x : x + (this.unitWidth + this.gutter) * 2,
34405 if(box[0].size == 'xs' && box[1].size == 'xs'){
34414 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34418 x : x + (this.unitWidth + this.gutter) * 1,
34432 x : x + (this.unitWidth + this.gutter) * 2,
34437 x : x + (this.unitWidth + this.gutter) * 2,
34438 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34445 getVerticalFourBoxColPositions : function(x, y, box)
34449 if(box[0].size == 'xs'){
34458 y : y + (this.unitHeight + this.gutter) * 1
34463 y : y + (this.unitHeight + this.gutter) * 2
34467 x : x + (this.unitWidth + this.gutter) * 1,
34481 x : x + (this.unitWidth + this.gutter) * 2,
34486 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34487 y : y + (this.unitHeight + this.gutter) * 1
34491 x : x + (this.unitWidth + this.gutter) * 2,
34492 y : y + (this.unitWidth + this.gutter) * 2
34499 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34503 if(box[0].size == 'md-left'){
34505 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34512 if(box[0].size == 'md-right'){
34514 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34515 y : minY + (this.unitWidth + this.gutter) * 1
34521 var rand = Math.floor(Math.random() * (4 - box[0].y));
34524 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34525 y : minY + (this.unitWidth + this.gutter) * rand
34532 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34536 if(box[0].size == 'xs'){
34539 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34544 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34545 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34553 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34558 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34559 y : minY + (this.unitWidth + this.gutter) * 2
34566 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34570 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34573 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34578 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34579 y : minY + (this.unitWidth + this.gutter) * 1
34583 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34584 y : minY + (this.unitWidth + this.gutter) * 2
34591 if(box[0].size == 'xs' && box[1].size == 'xs'){
34594 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34599 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34604 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34605 y : minY + (this.unitWidth + this.gutter) * 1
34613 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34618 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34619 y : minY + (this.unitWidth + this.gutter) * 2
34623 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34624 y : minY + (this.unitWidth + this.gutter) * 2
34631 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34635 if(box[0].size == 'xs'){
34638 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34643 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34648 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),
34653 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34654 y : minY + (this.unitWidth + this.gutter) * 1
34662 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34667 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34668 y : minY + (this.unitWidth + this.gutter) * 2
34672 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34673 y : minY + (this.unitWidth + this.gutter) * 2
34677 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),
34678 y : minY + (this.unitWidth + this.gutter) * 2
34686 * remove a Masonry Brick
34687 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34689 removeBrick : function(brick_id)
34695 for (var i = 0; i<this.bricks.length; i++) {
34696 if (this.bricks[i].id == brick_id) {
34697 this.bricks.splice(i,1);
34698 this.el.dom.removeChild(Roo.get(brick_id).dom);
34705 * adds a Masonry Brick
34706 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34708 addBrick : function(cfg)
34710 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34711 //this.register(cn);
34712 cn.parentId = this.id;
34713 cn.render(this.el);
34718 * register a Masonry Brick
34719 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34722 register : function(brick)
34724 this.bricks.push(brick);
34725 brick.masonryId = this.id;
34729 * clear all the Masonry Brick
34731 clearAll : function()
34734 //this.getChildContainer().dom.innerHTML = "";
34735 this.el.dom.innerHTML = '';
34738 getSelected : function()
34740 if (!this.selectedBrick) {
34744 return this.selectedBrick;
34748 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34752 * register a Masonry Layout
34753 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34756 register : function(layout)
34758 this.groups[layout.id] = layout;
34761 * fetch a Masonry Layout based on the masonry layout ID
34762 * @param {string} the masonry layout to add
34763 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34766 get: function(layout_id) {
34767 if (typeof(this.groups[layout_id]) == 'undefined') {
34770 return this.groups[layout_id] ;
34782 * http://masonry.desandro.com
34784 * The idea is to render all the bricks based on vertical width...
34786 * The original code extends 'outlayer' - we might need to use that....
34792 * @class Roo.bootstrap.LayoutMasonryAuto
34793 * @extends Roo.bootstrap.Component
34794 * Bootstrap Layout Masonry class
34797 * Create a new Element
34798 * @param {Object} config The config object
34801 Roo.bootstrap.LayoutMasonryAuto = function(config){
34802 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34805 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34808 * @cfg {Boolean} isFitWidth - resize the width..
34810 isFitWidth : false, // options..
34812 * @cfg {Boolean} isOriginLeft = left align?
34814 isOriginLeft : true,
34816 * @cfg {Boolean} isOriginTop = top align?
34818 isOriginTop : false,
34820 * @cfg {Boolean} isLayoutInstant = no animation?
34822 isLayoutInstant : false, // needed?
34824 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34826 isResizingContainer : true,
34828 * @cfg {Number} columnWidth width of the columns
34834 * @cfg {Number} maxCols maximum number of columns
34839 * @cfg {Number} padHeight padding below box..
34845 * @cfg {Boolean} isAutoInitial defalut true
34848 isAutoInitial : true,
34854 initialColumnWidth : 0,
34855 currentSize : null,
34857 colYs : null, // array.
34864 bricks: null, //CompositeElement
34865 cols : 0, // array?
34866 // element : null, // wrapped now this.el
34867 _isLayoutInited : null,
34870 getAutoCreate : function(){
34874 cls: 'blog-masonary-wrapper ' + this.cls,
34876 cls : 'mas-boxes masonary'
34883 getChildContainer: function( )
34885 if (this.boxesEl) {
34886 return this.boxesEl;
34889 this.boxesEl = this.el.select('.mas-boxes').first();
34891 return this.boxesEl;
34895 initEvents : function()
34899 if(this.isAutoInitial){
34900 Roo.log('hook children rendered');
34901 this.on('childrenrendered', function() {
34902 Roo.log('children rendered');
34909 initial : function()
34911 this.reloadItems();
34913 this.currentSize = this.el.getBox(true);
34915 /// was window resize... - let's see if this works..
34916 Roo.EventManager.onWindowResize(this.resize, this);
34918 if(!this.isAutoInitial){
34923 this.layout.defer(500,this);
34926 reloadItems: function()
34928 this.bricks = this.el.select('.masonry-brick', true);
34930 this.bricks.each(function(b) {
34931 //Roo.log(b.getSize());
34932 if (!b.attr('originalwidth')) {
34933 b.attr('originalwidth', b.getSize().width);
34938 Roo.log(this.bricks.elements.length);
34941 resize : function()
34944 var cs = this.el.getBox(true);
34946 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34947 Roo.log("no change in with or X");
34950 this.currentSize = cs;
34954 layout : function()
34957 this._resetLayout();
34958 //this._manageStamps();
34960 // don't animate first layout
34961 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34962 this.layoutItems( isInstant );
34964 // flag for initalized
34965 this._isLayoutInited = true;
34968 layoutItems : function( isInstant )
34970 //var items = this._getItemsForLayout( this.items );
34971 // original code supports filtering layout items.. we just ignore it..
34973 this._layoutItems( this.bricks , isInstant );
34975 this._postLayout();
34977 _layoutItems : function ( items , isInstant)
34979 //this.fireEvent( 'layout', this, items );
34982 if ( !items || !items.elements.length ) {
34983 // no items, emit event with empty array
34988 items.each(function(item) {
34989 Roo.log("layout item");
34991 // get x/y object from method
34992 var position = this._getItemLayoutPosition( item );
34994 position.item = item;
34995 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34996 queue.push( position );
34999 this._processLayoutQueue( queue );
35001 /** Sets position of item in DOM
35002 * @param {Element} item
35003 * @param {Number} x - horizontal position
35004 * @param {Number} y - vertical position
35005 * @param {Boolean} isInstant - disables transitions
35007 _processLayoutQueue : function( queue )
35009 for ( var i=0, len = queue.length; i < len; i++ ) {
35010 var obj = queue[i];
35011 obj.item.position('absolute');
35012 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35018 * Any logic you want to do after each layout,
35019 * i.e. size the container
35021 _postLayout : function()
35023 this.resizeContainer();
35026 resizeContainer : function()
35028 if ( !this.isResizingContainer ) {
35031 var size = this._getContainerSize();
35033 this.el.setSize(size.width,size.height);
35034 this.boxesEl.setSize(size.width,size.height);
35040 _resetLayout : function()
35042 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35043 this.colWidth = this.el.getWidth();
35044 //this.gutter = this.el.getWidth();
35046 this.measureColumns();
35052 this.colYs.push( 0 );
35058 measureColumns : function()
35060 this.getContainerWidth();
35061 // if columnWidth is 0, default to outerWidth of first item
35062 if ( !this.columnWidth ) {
35063 var firstItem = this.bricks.first();
35064 Roo.log(firstItem);
35065 this.columnWidth = this.containerWidth;
35066 if (firstItem && firstItem.attr('originalwidth') ) {
35067 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35069 // columnWidth fall back to item of first element
35070 Roo.log("set column width?");
35071 this.initialColumnWidth = this.columnWidth ;
35073 // if first elem has no width, default to size of container
35078 if (this.initialColumnWidth) {
35079 this.columnWidth = this.initialColumnWidth;
35084 // column width is fixed at the top - however if container width get's smaller we should
35087 // this bit calcs how man columns..
35089 var columnWidth = this.columnWidth += this.gutter;
35091 // calculate columns
35092 var containerWidth = this.containerWidth + this.gutter;
35094 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35095 // fix rounding errors, typically with gutters
35096 var excess = columnWidth - containerWidth % columnWidth;
35099 // if overshoot is less than a pixel, round up, otherwise floor it
35100 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35101 cols = Math[ mathMethod ]( cols );
35102 this.cols = Math.max( cols, 1 );
35103 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35105 // padding positioning..
35106 var totalColWidth = this.cols * this.columnWidth;
35107 var padavail = this.containerWidth - totalColWidth;
35108 // so for 2 columns - we need 3 'pads'
35110 var padNeeded = (1+this.cols) * this.padWidth;
35112 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35114 this.columnWidth += padExtra
35115 //this.padWidth = Math.floor(padavail / ( this.cols));
35117 // adjust colum width so that padding is fixed??
35119 // we have 3 columns ... total = width * 3
35120 // we have X left over... that should be used by
35122 //if (this.expandC) {
35130 getContainerWidth : function()
35132 /* // container is parent if fit width
35133 var container = this.isFitWidth ? this.element.parentNode : this.element;
35134 // check that this.size and size are there
35135 // IE8 triggers resize on body size change, so they might not be
35137 var size = getSize( container ); //FIXME
35138 this.containerWidth = size && size.innerWidth; //FIXME
35141 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35145 _getItemLayoutPosition : function( item ) // what is item?
35147 // we resize the item to our columnWidth..
35149 item.setWidth(this.columnWidth);
35150 item.autoBoxAdjust = false;
35152 var sz = item.getSize();
35154 // how many columns does this brick span
35155 var remainder = this.containerWidth % this.columnWidth;
35157 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35158 // round if off by 1 pixel, otherwise use ceil
35159 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35160 colSpan = Math.min( colSpan, this.cols );
35162 // normally this should be '1' as we dont' currently allow multi width columns..
35164 var colGroup = this._getColGroup( colSpan );
35165 // get the minimum Y value from the columns
35166 var minimumY = Math.min.apply( Math, colGroup );
35167 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35169 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35171 // position the brick
35173 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35174 y: this.currentSize.y + minimumY + this.padHeight
35178 // apply setHeight to necessary columns
35179 var setHeight = minimumY + sz.height + this.padHeight;
35180 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35182 var setSpan = this.cols + 1 - colGroup.length;
35183 for ( var i = 0; i < setSpan; i++ ) {
35184 this.colYs[ shortColIndex + i ] = setHeight ;
35191 * @param {Number} colSpan - number of columns the element spans
35192 * @returns {Array} colGroup
35194 _getColGroup : function( colSpan )
35196 if ( colSpan < 2 ) {
35197 // if brick spans only one column, use all the column Ys
35202 // how many different places could this brick fit horizontally
35203 var groupCount = this.cols + 1 - colSpan;
35204 // for each group potential horizontal position
35205 for ( var i = 0; i < groupCount; i++ ) {
35206 // make an array of colY values for that one group
35207 var groupColYs = this.colYs.slice( i, i + colSpan );
35208 // and get the max value of the array
35209 colGroup[i] = Math.max.apply( Math, groupColYs );
35214 _manageStamp : function( stamp )
35216 var stampSize = stamp.getSize();
35217 var offset = stamp.getBox();
35218 // get the columns that this stamp affects
35219 var firstX = this.isOriginLeft ? offset.x : offset.right;
35220 var lastX = firstX + stampSize.width;
35221 var firstCol = Math.floor( firstX / this.columnWidth );
35222 firstCol = Math.max( 0, firstCol );
35224 var lastCol = Math.floor( lastX / this.columnWidth );
35225 // lastCol should not go over if multiple of columnWidth #425
35226 lastCol -= lastX % this.columnWidth ? 0 : 1;
35227 lastCol = Math.min( this.cols - 1, lastCol );
35229 // set colYs to bottom of the stamp
35230 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35233 for ( var i = firstCol; i <= lastCol; i++ ) {
35234 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35239 _getContainerSize : function()
35241 this.maxY = Math.max.apply( Math, this.colYs );
35246 if ( this.isFitWidth ) {
35247 size.width = this._getContainerFitWidth();
35253 _getContainerFitWidth : function()
35255 var unusedCols = 0;
35256 // count unused columns
35259 if ( this.colYs[i] !== 0 ) {
35264 // fit container to columns that have been used
35265 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35268 needsResizeLayout : function()
35270 var previousWidth = this.containerWidth;
35271 this.getContainerWidth();
35272 return previousWidth !== this.containerWidth;
35287 * @class Roo.bootstrap.MasonryBrick
35288 * @extends Roo.bootstrap.Component
35289 * Bootstrap MasonryBrick class
35292 * Create a new MasonryBrick
35293 * @param {Object} config The config object
35296 Roo.bootstrap.MasonryBrick = function(config){
35298 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35300 Roo.bootstrap.MasonryBrick.register(this);
35306 * When a MasonryBrick is clcik
35307 * @param {Roo.bootstrap.MasonryBrick} this
35308 * @param {Roo.EventObject} e
35314 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35317 * @cfg {String} title
35321 * @cfg {String} html
35325 * @cfg {String} bgimage
35329 * @cfg {String} videourl
35333 * @cfg {String} cls
35337 * @cfg {String} href
35341 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35346 * @cfg {String} placetitle (center|bottom)
35351 * @cfg {Boolean} isFitContainer defalut true
35353 isFitContainer : true,
35356 * @cfg {Boolean} preventDefault defalut false
35358 preventDefault : false,
35361 * @cfg {Boolean} inverse defalut false
35363 maskInverse : false,
35365 getAutoCreate : function()
35367 if(!this.isFitContainer){
35368 return this.getSplitAutoCreate();
35371 var cls = 'masonry-brick masonry-brick-full';
35373 if(this.href.length){
35374 cls += ' masonry-brick-link';
35377 if(this.bgimage.length){
35378 cls += ' masonry-brick-image';
35381 if(this.maskInverse){
35382 cls += ' mask-inverse';
35385 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35386 cls += ' enable-mask';
35390 cls += ' masonry-' + this.size + '-brick';
35393 if(this.placetitle.length){
35395 switch (this.placetitle) {
35397 cls += ' masonry-center-title';
35400 cls += ' masonry-bottom-title';
35407 if(!this.html.length && !this.bgimage.length){
35408 cls += ' masonry-center-title';
35411 if(!this.html.length && this.bgimage.length){
35412 cls += ' masonry-bottom-title';
35417 cls += ' ' + this.cls;
35421 tag: (this.href.length) ? 'a' : 'div',
35426 cls: 'masonry-brick-mask'
35430 cls: 'masonry-brick-paragraph',
35436 if(this.href.length){
35437 cfg.href = this.href;
35440 var cn = cfg.cn[1].cn;
35442 if(this.title.length){
35445 cls: 'masonry-brick-title',
35450 if(this.html.length){
35453 cls: 'masonry-brick-text',
35458 if (!this.title.length && !this.html.length) {
35459 cfg.cn[1].cls += ' hide';
35462 if(this.bgimage.length){
35465 cls: 'masonry-brick-image-view',
35470 if(this.videourl.length){
35471 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35472 // youtube support only?
35475 cls: 'masonry-brick-image-view',
35478 allowfullscreen : true
35486 getSplitAutoCreate : function()
35488 var cls = 'masonry-brick masonry-brick-split';
35490 if(this.href.length){
35491 cls += ' masonry-brick-link';
35494 if(this.bgimage.length){
35495 cls += ' masonry-brick-image';
35499 cls += ' masonry-' + this.size + '-brick';
35502 switch (this.placetitle) {
35504 cls += ' masonry-center-title';
35507 cls += ' masonry-bottom-title';
35510 if(!this.bgimage.length){
35511 cls += ' masonry-center-title';
35514 if(this.bgimage.length){
35515 cls += ' masonry-bottom-title';
35521 cls += ' ' + this.cls;
35525 tag: (this.href.length) ? 'a' : 'div',
35530 cls: 'masonry-brick-split-head',
35534 cls: 'masonry-brick-paragraph',
35541 cls: 'masonry-brick-split-body',
35547 if(this.href.length){
35548 cfg.href = this.href;
35551 if(this.title.length){
35552 cfg.cn[0].cn[0].cn.push({
35554 cls: 'masonry-brick-title',
35559 if(this.html.length){
35560 cfg.cn[1].cn.push({
35562 cls: 'masonry-brick-text',
35567 if(this.bgimage.length){
35568 cfg.cn[0].cn.push({
35570 cls: 'masonry-brick-image-view',
35575 if(this.videourl.length){
35576 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35577 // youtube support only?
35578 cfg.cn[0].cn.cn.push({
35580 cls: 'masonry-brick-image-view',
35583 allowfullscreen : true
35590 initEvents: function()
35592 switch (this.size) {
35625 this.el.on('touchstart', this.onTouchStart, this);
35626 this.el.on('touchmove', this.onTouchMove, this);
35627 this.el.on('touchend', this.onTouchEnd, this);
35628 this.el.on('contextmenu', this.onContextMenu, this);
35630 this.el.on('mouseenter' ,this.enter, this);
35631 this.el.on('mouseleave', this.leave, this);
35632 this.el.on('click', this.onClick, this);
35635 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35636 this.parent().bricks.push(this);
35641 onClick: function(e, el)
35643 var time = this.endTimer - this.startTimer;
35644 // Roo.log(e.preventDefault());
35647 e.preventDefault();
35652 if(!this.preventDefault){
35656 e.preventDefault();
35658 if (this.activeClass != '') {
35659 this.selectBrick();
35662 this.fireEvent('click', this, e);
35665 enter: function(e, el)
35667 e.preventDefault();
35669 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35673 if(this.bgimage.length && this.html.length){
35674 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35678 leave: function(e, el)
35680 e.preventDefault();
35682 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35686 if(this.bgimage.length && this.html.length){
35687 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35691 onTouchStart: function(e, el)
35693 // e.preventDefault();
35695 this.touchmoved = false;
35697 if(!this.isFitContainer){
35701 if(!this.bgimage.length || !this.html.length){
35705 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35707 this.timer = new Date().getTime();
35711 onTouchMove: function(e, el)
35713 this.touchmoved = true;
35716 onContextMenu : function(e,el)
35718 e.preventDefault();
35719 e.stopPropagation();
35723 onTouchEnd: function(e, el)
35725 // e.preventDefault();
35727 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35734 if(!this.bgimage.length || !this.html.length){
35736 if(this.href.length){
35737 window.location.href = this.href;
35743 if(!this.isFitContainer){
35747 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35749 window.location.href = this.href;
35752 //selection on single brick only
35753 selectBrick : function() {
35755 if (!this.parentId) {
35759 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35760 var index = m.selectedBrick.indexOf(this.id);
35763 m.selectedBrick.splice(index,1);
35764 this.el.removeClass(this.activeClass);
35768 for(var i = 0; i < m.selectedBrick.length; i++) {
35769 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35770 b.el.removeClass(b.activeClass);
35773 m.selectedBrick = [];
35775 m.selectedBrick.push(this.id);
35776 this.el.addClass(this.activeClass);
35780 isSelected : function(){
35781 return this.el.hasClass(this.activeClass);
35786 Roo.apply(Roo.bootstrap.MasonryBrick, {
35789 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35791 * register a Masonry Brick
35792 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35795 register : function(brick)
35797 //this.groups[brick.id] = brick;
35798 this.groups.add(brick.id, brick);
35801 * fetch a masonry brick based on the masonry brick ID
35802 * @param {string} the masonry brick to add
35803 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35806 get: function(brick_id)
35808 // if (typeof(this.groups[brick_id]) == 'undefined') {
35811 // return this.groups[brick_id] ;
35813 if(this.groups.key(brick_id)) {
35814 return this.groups.key(brick_id);
35832 * @class Roo.bootstrap.Brick
35833 * @extends Roo.bootstrap.Component
35834 * Bootstrap Brick class
35837 * Create a new Brick
35838 * @param {Object} config The config object
35841 Roo.bootstrap.Brick = function(config){
35842 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35848 * When a Brick is click
35849 * @param {Roo.bootstrap.Brick} this
35850 * @param {Roo.EventObject} e
35856 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35859 * @cfg {String} title
35863 * @cfg {String} html
35867 * @cfg {String} bgimage
35871 * @cfg {String} cls
35875 * @cfg {String} href
35879 * @cfg {String} video
35883 * @cfg {Boolean} square
35887 getAutoCreate : function()
35889 var cls = 'roo-brick';
35891 if(this.href.length){
35892 cls += ' roo-brick-link';
35895 if(this.bgimage.length){
35896 cls += ' roo-brick-image';
35899 if(!this.html.length && !this.bgimage.length){
35900 cls += ' roo-brick-center-title';
35903 if(!this.html.length && this.bgimage.length){
35904 cls += ' roo-brick-bottom-title';
35908 cls += ' ' + this.cls;
35912 tag: (this.href.length) ? 'a' : 'div',
35917 cls: 'roo-brick-paragraph',
35923 if(this.href.length){
35924 cfg.href = this.href;
35927 var cn = cfg.cn[0].cn;
35929 if(this.title.length){
35932 cls: 'roo-brick-title',
35937 if(this.html.length){
35940 cls: 'roo-brick-text',
35947 if(this.bgimage.length){
35950 cls: 'roo-brick-image-view',
35958 initEvents: function()
35960 if(this.title.length || this.html.length){
35961 this.el.on('mouseenter' ,this.enter, this);
35962 this.el.on('mouseleave', this.leave, this);
35965 Roo.EventManager.onWindowResize(this.resize, this);
35967 if(this.bgimage.length){
35968 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35969 this.imageEl.on('load', this.onImageLoad, this);
35976 onImageLoad : function()
35981 resize : function()
35983 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35985 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35987 if(this.bgimage.length){
35988 var image = this.el.select('.roo-brick-image-view', true).first();
35990 image.setWidth(paragraph.getWidth());
35993 image.setHeight(paragraph.getWidth());
35996 this.el.setHeight(image.getHeight());
35997 paragraph.setHeight(image.getHeight());
36003 enter: function(e, el)
36005 e.preventDefault();
36007 if(this.bgimage.length){
36008 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36009 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36013 leave: function(e, el)
36015 e.preventDefault();
36017 if(this.bgimage.length){
36018 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36019 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36034 * @class Roo.bootstrap.NumberField
36035 * @extends Roo.bootstrap.Input
36036 * Bootstrap NumberField class
36042 * Create a new NumberField
36043 * @param {Object} config The config object
36046 Roo.bootstrap.NumberField = function(config){
36047 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36050 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36053 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36055 allowDecimals : true,
36057 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36059 decimalSeparator : ".",
36061 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36063 decimalPrecision : 2,
36065 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36067 allowNegative : true,
36070 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36074 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36076 minValue : Number.NEGATIVE_INFINITY,
36078 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36080 maxValue : Number.MAX_VALUE,
36082 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36084 minText : "The minimum value for this field is {0}",
36086 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36088 maxText : "The maximum value for this field is {0}",
36090 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36091 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36093 nanText : "{0} is not a valid number",
36095 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36097 thousandsDelimiter : false,
36099 * @cfg {String} valueAlign alignment of value
36101 valueAlign : "left",
36103 getAutoCreate : function()
36105 var hiddenInput = {
36109 cls: 'hidden-number-input'
36113 hiddenInput.name = this.name;
36118 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36120 this.name = hiddenInput.name;
36122 if(cfg.cn.length > 0) {
36123 cfg.cn.push(hiddenInput);
36130 initEvents : function()
36132 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36134 var allowed = "0123456789";
36136 if(this.allowDecimals){
36137 allowed += this.decimalSeparator;
36140 if(this.allowNegative){
36144 if(this.thousandsDelimiter) {
36148 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36150 var keyPress = function(e){
36152 var k = e.getKey();
36154 var c = e.getCharCode();
36157 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36158 allowed.indexOf(String.fromCharCode(c)) === -1
36164 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36168 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36173 this.el.on("keypress", keyPress, this);
36176 validateValue : function(value)
36179 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36183 var num = this.parseValue(value);
36186 this.markInvalid(String.format(this.nanText, value));
36190 if(num < this.minValue){
36191 this.markInvalid(String.format(this.minText, this.minValue));
36195 if(num > this.maxValue){
36196 this.markInvalid(String.format(this.maxText, this.maxValue));
36203 getValue : function()
36205 var v = this.hiddenEl().getValue();
36207 return this.fixPrecision(this.parseValue(v));
36210 parseValue : function(value)
36212 if(this.thousandsDelimiter) {
36214 r = new RegExp(",", "g");
36215 value = value.replace(r, "");
36218 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36219 return isNaN(value) ? '' : value;
36222 fixPrecision : function(value)
36224 if(this.thousandsDelimiter) {
36226 r = new RegExp(",", "g");
36227 value = value.replace(r, "");
36230 var nan = isNaN(value);
36232 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36233 return nan ? '' : value;
36235 return parseFloat(value).toFixed(this.decimalPrecision);
36238 setValue : function(v)
36240 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36246 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36248 this.inputEl().dom.value = (v == '') ? '' :
36249 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36251 if(!this.allowZero && v === '0') {
36252 this.hiddenEl().dom.value = '';
36253 this.inputEl().dom.value = '';
36260 decimalPrecisionFcn : function(v)
36262 return Math.floor(v);
36265 beforeBlur : function()
36267 var v = this.parseValue(this.getRawValue());
36269 if(v || v === 0 || v === ''){
36274 hiddenEl : function()
36276 return this.el.select('input.hidden-number-input',true).first();
36288 * @class Roo.bootstrap.DocumentSlider
36289 * @extends Roo.bootstrap.Component
36290 * Bootstrap DocumentSlider class
36293 * Create a new DocumentViewer
36294 * @param {Object} config The config object
36297 Roo.bootstrap.DocumentSlider = function(config){
36298 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36305 * Fire after initEvent
36306 * @param {Roo.bootstrap.DocumentSlider} this
36311 * Fire after update
36312 * @param {Roo.bootstrap.DocumentSlider} this
36318 * @param {Roo.bootstrap.DocumentSlider} this
36324 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36330 getAutoCreate : function()
36334 cls : 'roo-document-slider',
36338 cls : 'roo-document-slider-header',
36342 cls : 'roo-document-slider-header-title'
36348 cls : 'roo-document-slider-body',
36352 cls : 'roo-document-slider-prev',
36356 cls : 'fa fa-chevron-left'
36362 cls : 'roo-document-slider-thumb',
36366 cls : 'roo-document-slider-image'
36372 cls : 'roo-document-slider-next',
36376 cls : 'fa fa-chevron-right'
36388 initEvents : function()
36390 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36391 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36393 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36394 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36396 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36397 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36399 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36400 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36402 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36403 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36405 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36406 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36408 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36409 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36411 this.thumbEl.on('click', this.onClick, this);
36413 this.prevIndicator.on('click', this.prev, this);
36415 this.nextIndicator.on('click', this.next, this);
36419 initial : function()
36421 if(this.files.length){
36422 this.indicator = 1;
36426 this.fireEvent('initial', this);
36429 update : function()
36431 this.imageEl.attr('src', this.files[this.indicator - 1]);
36433 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36435 this.prevIndicator.show();
36437 if(this.indicator == 1){
36438 this.prevIndicator.hide();
36441 this.nextIndicator.show();
36443 if(this.indicator == this.files.length){
36444 this.nextIndicator.hide();
36447 this.thumbEl.scrollTo('top');
36449 this.fireEvent('update', this);
36452 onClick : function(e)
36454 e.preventDefault();
36456 this.fireEvent('click', this);
36461 e.preventDefault();
36463 this.indicator = Math.max(1, this.indicator - 1);
36470 e.preventDefault();
36472 this.indicator = Math.min(this.files.length, this.indicator + 1);
36486 * @class Roo.bootstrap.RadioSet
36487 * @extends Roo.bootstrap.Input
36488 * Bootstrap RadioSet class
36489 * @cfg {String} indicatorpos (left|right) default left
36490 * @cfg {Boolean} inline (true|false) inline the element (default true)
36491 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36493 * Create a new RadioSet
36494 * @param {Object} config The config object
36497 Roo.bootstrap.RadioSet = function(config){
36499 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36503 Roo.bootstrap.RadioSet.register(this);
36508 * Fires when the element is checked or unchecked.
36509 * @param {Roo.bootstrap.RadioSet} this This radio
36510 * @param {Roo.bootstrap.Radio} item The checked item
36515 * Fires when the element is click.
36516 * @param {Roo.bootstrap.RadioSet} this This radio set
36517 * @param {Roo.bootstrap.Radio} item The checked item
36518 * @param {Roo.EventObject} e The event object
36525 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36533 indicatorpos : 'left',
36535 getAutoCreate : function()
36539 cls : 'roo-radio-set-label',
36543 html : this.fieldLabel
36547 if (Roo.bootstrap.version == 3) {
36550 if(this.indicatorpos == 'left'){
36553 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36554 tooltip : 'This field is required'
36559 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36560 tooltip : 'This field is required'
36566 cls : 'roo-radio-set-items'
36569 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36571 if (align === 'left' && this.fieldLabel.length) {
36574 cls : "roo-radio-set-right",
36580 if(this.labelWidth > 12){
36581 label.style = "width: " + this.labelWidth + 'px';
36584 if(this.labelWidth < 13 && this.labelmd == 0){
36585 this.labelmd = this.labelWidth;
36588 if(this.labellg > 0){
36589 label.cls += ' col-lg-' + this.labellg;
36590 items.cls += ' col-lg-' + (12 - this.labellg);
36593 if(this.labelmd > 0){
36594 label.cls += ' col-md-' + this.labelmd;
36595 items.cls += ' col-md-' + (12 - this.labelmd);
36598 if(this.labelsm > 0){
36599 label.cls += ' col-sm-' + this.labelsm;
36600 items.cls += ' col-sm-' + (12 - this.labelsm);
36603 if(this.labelxs > 0){
36604 label.cls += ' col-xs-' + this.labelxs;
36605 items.cls += ' col-xs-' + (12 - this.labelxs);
36611 cls : 'roo-radio-set',
36615 cls : 'roo-radio-set-input',
36618 value : this.value ? this.value : ''
36625 if(this.weight.length){
36626 cfg.cls += ' roo-radio-' + this.weight;
36630 cfg.cls += ' roo-radio-set-inline';
36634 ['xs','sm','md','lg'].map(function(size){
36635 if (settings[size]) {
36636 cfg.cls += ' col-' + size + '-' + settings[size];
36644 initEvents : function()
36646 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36647 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36649 if(!this.fieldLabel.length){
36650 this.labelEl.hide();
36653 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36654 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36656 this.indicator = this.indicatorEl();
36658 if(this.indicator){
36659 this.indicator.addClass('invisible');
36662 this.originalValue = this.getValue();
36666 inputEl: function ()
36668 return this.el.select('.roo-radio-set-input', true).first();
36671 getChildContainer : function()
36673 return this.itemsEl;
36676 register : function(item)
36678 this.radioes.push(item);
36682 validate : function()
36684 if(this.getVisibilityEl().hasClass('hidden')){
36690 Roo.each(this.radioes, function(i){
36699 if(this.allowBlank) {
36703 if(this.disabled || valid){
36708 this.markInvalid();
36713 markValid : function()
36715 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36716 this.indicatorEl().removeClass('visible');
36717 this.indicatorEl().addClass('invisible');
36721 if (Roo.bootstrap.version == 3) {
36722 this.el.removeClass([this.invalidClass, this.validClass]);
36723 this.el.addClass(this.validClass);
36725 this.el.removeClass(['is-invalid','is-valid']);
36726 this.el.addClass(['is-valid']);
36728 this.fireEvent('valid', this);
36731 markInvalid : function(msg)
36733 if(this.allowBlank || this.disabled){
36737 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36738 this.indicatorEl().removeClass('invisible');
36739 this.indicatorEl().addClass('visible');
36741 if (Roo.bootstrap.version == 3) {
36742 this.el.removeClass([this.invalidClass, this.validClass]);
36743 this.el.addClass(this.invalidClass);
36745 this.el.removeClass(['is-invalid','is-valid']);
36746 this.el.addClass(['is-invalid']);
36749 this.fireEvent('invalid', this, msg);
36753 setValue : function(v, suppressEvent)
36755 if(this.value === v){
36762 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36765 Roo.each(this.radioes, function(i){
36767 i.el.removeClass('checked');
36770 Roo.each(this.radioes, function(i){
36772 if(i.value === v || i.value.toString() === v.toString()){
36774 i.el.addClass('checked');
36776 if(suppressEvent !== true){
36777 this.fireEvent('check', this, i);
36788 clearInvalid : function(){
36790 if(!this.el || this.preventMark){
36794 this.el.removeClass([this.invalidClass]);
36796 this.fireEvent('valid', this);
36801 Roo.apply(Roo.bootstrap.RadioSet, {
36805 register : function(set)
36807 this.groups[set.name] = set;
36810 get: function(name)
36812 if (typeof(this.groups[name]) == 'undefined') {
36816 return this.groups[name] ;
36822 * Ext JS Library 1.1.1
36823 * Copyright(c) 2006-2007, Ext JS, LLC.
36825 * Originally Released Under LGPL - original licence link has changed is not relivant.
36828 * <script type="text/javascript">
36833 * @class Roo.bootstrap.SplitBar
36834 * @extends Roo.util.Observable
36835 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36839 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36840 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36841 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36842 split.minSize = 100;
36843 split.maxSize = 600;
36844 split.animate = true;
36845 split.on('moved', splitterMoved);
36848 * Create a new SplitBar
36849 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36850 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36851 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36852 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36853 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36854 position of the SplitBar).
36856 Roo.bootstrap.SplitBar = function(cfg){
36861 // dragElement : elm
36862 // resizingElement: el,
36864 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36865 // placement : Roo.bootstrap.SplitBar.LEFT ,
36866 // existingProxy ???
36869 this.el = Roo.get(cfg.dragElement, true);
36870 this.el.dom.unselectable = "on";
36872 this.resizingEl = Roo.get(cfg.resizingElement, true);
36876 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36877 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36880 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36883 * The minimum size of the resizing element. (Defaults to 0)
36889 * The maximum size of the resizing element. (Defaults to 2000)
36892 this.maxSize = 2000;
36895 * Whether to animate the transition to the new size
36898 this.animate = false;
36901 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36904 this.useShim = false;
36909 if(!cfg.existingProxy){
36911 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36913 this.proxy = Roo.get(cfg.existingProxy).dom;
36916 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36919 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36922 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36925 this.dragSpecs = {};
36928 * @private The adapter to use to positon and resize elements
36930 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36931 this.adapter.init(this);
36933 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36935 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36936 this.el.addClass("roo-splitbar-h");
36939 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36940 this.el.addClass("roo-splitbar-v");
36946 * Fires when the splitter is moved (alias for {@link #event-moved})
36947 * @param {Roo.bootstrap.SplitBar} this
36948 * @param {Number} newSize the new width or height
36953 * Fires when the splitter is moved
36954 * @param {Roo.bootstrap.SplitBar} this
36955 * @param {Number} newSize the new width or height
36959 * @event beforeresize
36960 * Fires before the splitter is dragged
36961 * @param {Roo.bootstrap.SplitBar} this
36963 "beforeresize" : true,
36965 "beforeapply" : true
36968 Roo.util.Observable.call(this);
36971 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36972 onStartProxyDrag : function(x, y){
36973 this.fireEvent("beforeresize", this);
36975 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36977 o.enableDisplayMode("block");
36978 // all splitbars share the same overlay
36979 Roo.bootstrap.SplitBar.prototype.overlay = o;
36981 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36982 this.overlay.show();
36983 Roo.get(this.proxy).setDisplayed("block");
36984 var size = this.adapter.getElementSize(this);
36985 this.activeMinSize = this.getMinimumSize();;
36986 this.activeMaxSize = this.getMaximumSize();;
36987 var c1 = size - this.activeMinSize;
36988 var c2 = Math.max(this.activeMaxSize - size, 0);
36989 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36990 this.dd.resetConstraints();
36991 this.dd.setXConstraint(
36992 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36993 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36995 this.dd.setYConstraint(0, 0);
36997 this.dd.resetConstraints();
36998 this.dd.setXConstraint(0, 0);
36999 this.dd.setYConstraint(
37000 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37001 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37004 this.dragSpecs.startSize = size;
37005 this.dragSpecs.startPoint = [x, y];
37006 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37010 * @private Called after the drag operation by the DDProxy
37012 onEndProxyDrag : function(e){
37013 Roo.get(this.proxy).setDisplayed(false);
37014 var endPoint = Roo.lib.Event.getXY(e);
37016 this.overlay.hide();
37019 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37020 newSize = this.dragSpecs.startSize +
37021 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37022 endPoint[0] - this.dragSpecs.startPoint[0] :
37023 this.dragSpecs.startPoint[0] - endPoint[0]
37026 newSize = this.dragSpecs.startSize +
37027 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37028 endPoint[1] - this.dragSpecs.startPoint[1] :
37029 this.dragSpecs.startPoint[1] - endPoint[1]
37032 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37033 if(newSize != this.dragSpecs.startSize){
37034 if(this.fireEvent('beforeapply', this, newSize) !== false){
37035 this.adapter.setElementSize(this, newSize);
37036 this.fireEvent("moved", this, newSize);
37037 this.fireEvent("resize", this, newSize);
37043 * Get the adapter this SplitBar uses
37044 * @return The adapter object
37046 getAdapter : function(){
37047 return this.adapter;
37051 * Set the adapter this SplitBar uses
37052 * @param {Object} adapter A SplitBar adapter object
37054 setAdapter : function(adapter){
37055 this.adapter = adapter;
37056 this.adapter.init(this);
37060 * Gets the minimum size for the resizing element
37061 * @return {Number} The minimum size
37063 getMinimumSize : function(){
37064 return this.minSize;
37068 * Sets the minimum size for the resizing element
37069 * @param {Number} minSize The minimum size
37071 setMinimumSize : function(minSize){
37072 this.minSize = minSize;
37076 * Gets the maximum size for the resizing element
37077 * @return {Number} The maximum size
37079 getMaximumSize : function(){
37080 return this.maxSize;
37084 * Sets the maximum size for the resizing element
37085 * @param {Number} maxSize The maximum size
37087 setMaximumSize : function(maxSize){
37088 this.maxSize = maxSize;
37092 * Sets the initialize size for the resizing element
37093 * @param {Number} size The initial size
37095 setCurrentSize : function(size){
37096 var oldAnimate = this.animate;
37097 this.animate = false;
37098 this.adapter.setElementSize(this, size);
37099 this.animate = oldAnimate;
37103 * Destroy this splitbar.
37104 * @param {Boolean} removeEl True to remove the element
37106 destroy : function(removeEl){
37108 this.shim.remove();
37111 this.proxy.parentNode.removeChild(this.proxy);
37119 * @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.
37121 Roo.bootstrap.SplitBar.createProxy = function(dir){
37122 var proxy = new Roo.Element(document.createElement("div"));
37123 proxy.unselectable();
37124 var cls = 'roo-splitbar-proxy';
37125 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37126 document.body.appendChild(proxy.dom);
37131 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37132 * Default Adapter. It assumes the splitter and resizing element are not positioned
37133 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37135 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37138 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37139 // do nothing for now
37140 init : function(s){
37144 * Called before drag operations to get the current size of the resizing element.
37145 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37147 getElementSize : function(s){
37148 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37149 return s.resizingEl.getWidth();
37151 return s.resizingEl.getHeight();
37156 * Called after drag operations to set the size of the resizing element.
37157 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37158 * @param {Number} newSize The new size to set
37159 * @param {Function} onComplete A function to be invoked when resizing is complete
37161 setElementSize : function(s, newSize, onComplete){
37162 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37164 s.resizingEl.setWidth(newSize);
37166 onComplete(s, newSize);
37169 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37174 s.resizingEl.setHeight(newSize);
37176 onComplete(s, newSize);
37179 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37186 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37187 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37188 * Adapter that moves the splitter element to align with the resized sizing element.
37189 * Used with an absolute positioned SplitBar.
37190 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37191 * document.body, make sure you assign an id to the body element.
37193 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37194 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37195 this.container = Roo.get(container);
37198 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37199 init : function(s){
37200 this.basic.init(s);
37203 getElementSize : function(s){
37204 return this.basic.getElementSize(s);
37207 setElementSize : function(s, newSize, onComplete){
37208 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37211 moveSplitter : function(s){
37212 var yes = Roo.bootstrap.SplitBar;
37213 switch(s.placement){
37215 s.el.setX(s.resizingEl.getRight());
37218 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37221 s.el.setY(s.resizingEl.getBottom());
37224 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37231 * Orientation constant - Create a vertical SplitBar
37235 Roo.bootstrap.SplitBar.VERTICAL = 1;
37238 * Orientation constant - Create a horizontal SplitBar
37242 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37245 * Placement constant - The resizing element is to the left of the splitter element
37249 Roo.bootstrap.SplitBar.LEFT = 1;
37252 * Placement constant - The resizing element is to the right of the splitter element
37256 Roo.bootstrap.SplitBar.RIGHT = 2;
37259 * Placement constant - The resizing element is positioned above the splitter element
37263 Roo.bootstrap.SplitBar.TOP = 3;
37266 * Placement constant - The resizing element is positioned under splitter element
37270 Roo.bootstrap.SplitBar.BOTTOM = 4;
37271 Roo.namespace("Roo.bootstrap.layout");/*
37273 * Ext JS Library 1.1.1
37274 * Copyright(c) 2006-2007, Ext JS, LLC.
37276 * Originally Released Under LGPL - original licence link has changed is not relivant.
37279 * <script type="text/javascript">
37283 * @class Roo.bootstrap.layout.Manager
37284 * @extends Roo.bootstrap.Component
37285 * Base class for layout managers.
37287 Roo.bootstrap.layout.Manager = function(config)
37289 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37295 /** false to disable window resize monitoring @type Boolean */
37296 this.monitorWindowResize = true;
37301 * Fires when a layout is performed.
37302 * @param {Roo.LayoutManager} this
37306 * @event regionresized
37307 * Fires when the user resizes a region.
37308 * @param {Roo.LayoutRegion} region The resized region
37309 * @param {Number} newSize The new size (width for east/west, height for north/south)
37311 "regionresized" : true,
37313 * @event regioncollapsed
37314 * Fires when a region is collapsed.
37315 * @param {Roo.LayoutRegion} region The collapsed region
37317 "regioncollapsed" : true,
37319 * @event regionexpanded
37320 * Fires when a region is expanded.
37321 * @param {Roo.LayoutRegion} region The expanded region
37323 "regionexpanded" : true
37325 this.updating = false;
37328 this.el = Roo.get(config.el);
37334 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37339 monitorWindowResize : true,
37345 onRender : function(ct, position)
37348 this.el = Roo.get(ct);
37351 //this.fireEvent('render',this);
37355 initEvents: function()
37359 // ie scrollbar fix
37360 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37361 document.body.scroll = "no";
37362 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37363 this.el.position('relative');
37365 this.id = this.el.id;
37366 this.el.addClass("roo-layout-container");
37367 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37368 if(this.el.dom != document.body ) {
37369 this.el.on('resize', this.layout,this);
37370 this.el.on('show', this.layout,this);
37376 * Returns true if this layout is currently being updated
37377 * @return {Boolean}
37379 isUpdating : function(){
37380 return this.updating;
37384 * Suspend the LayoutManager from doing auto-layouts while
37385 * making multiple add or remove calls
37387 beginUpdate : function(){
37388 this.updating = true;
37392 * Restore auto-layouts and optionally disable the manager from performing a layout
37393 * @param {Boolean} noLayout true to disable a layout update
37395 endUpdate : function(noLayout){
37396 this.updating = false;
37402 layout: function(){
37406 onRegionResized : function(region, newSize){
37407 this.fireEvent("regionresized", region, newSize);
37411 onRegionCollapsed : function(region){
37412 this.fireEvent("regioncollapsed", region);
37415 onRegionExpanded : function(region){
37416 this.fireEvent("regionexpanded", region);
37420 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37421 * performs box-model adjustments.
37422 * @return {Object} The size as an object {width: (the width), height: (the height)}
37424 getViewSize : function()
37427 if(this.el.dom != document.body){
37428 size = this.el.getSize();
37430 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37432 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37433 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37438 * Returns the Element this layout is bound to.
37439 * @return {Roo.Element}
37441 getEl : function(){
37446 * Returns the specified region.
37447 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37448 * @return {Roo.LayoutRegion}
37450 getRegion : function(target){
37451 return this.regions[target.toLowerCase()];
37454 onWindowResize : function(){
37455 if(this.monitorWindowResize){
37462 * Ext JS Library 1.1.1
37463 * Copyright(c) 2006-2007, Ext JS, LLC.
37465 * Originally Released Under LGPL - original licence link has changed is not relivant.
37468 * <script type="text/javascript">
37471 * @class Roo.bootstrap.layout.Border
37472 * @extends Roo.bootstrap.layout.Manager
37473 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37474 * please see: examples/bootstrap/nested.html<br><br>
37476 <b>The container the layout is rendered into can be either the body element or any other element.
37477 If it is not the body element, the container needs to either be an absolute positioned element,
37478 or you will need to add "position:relative" to the css of the container. You will also need to specify
37479 the container size if it is not the body element.</b>
37482 * Create a new Border
37483 * @param {Object} config Configuration options
37485 Roo.bootstrap.layout.Border = function(config){
37486 config = config || {};
37487 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37491 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37492 if(config[region]){
37493 config[region].region = region;
37494 this.addRegion(config[region]);
37500 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37502 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37504 parent : false, // this might point to a 'nest' or a ???
37507 * Creates and adds a new region if it doesn't already exist.
37508 * @param {String} target The target region key (north, south, east, west or center).
37509 * @param {Object} config The regions config object
37510 * @return {BorderLayoutRegion} The new region
37512 addRegion : function(config)
37514 if(!this.regions[config.region]){
37515 var r = this.factory(config);
37516 this.bindRegion(r);
37518 return this.regions[config.region];
37522 bindRegion : function(r){
37523 this.regions[r.config.region] = r;
37525 r.on("visibilitychange", this.layout, this);
37526 r.on("paneladded", this.layout, this);
37527 r.on("panelremoved", this.layout, this);
37528 r.on("invalidated", this.layout, this);
37529 r.on("resized", this.onRegionResized, this);
37530 r.on("collapsed", this.onRegionCollapsed, this);
37531 r.on("expanded", this.onRegionExpanded, this);
37535 * Performs a layout update.
37537 layout : function()
37539 if(this.updating) {
37543 // render all the rebions if they have not been done alreayd?
37544 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37545 if(this.regions[region] && !this.regions[region].bodyEl){
37546 this.regions[region].onRender(this.el)
37550 var size = this.getViewSize();
37551 var w = size.width;
37552 var h = size.height;
37557 //var x = 0, y = 0;
37559 var rs = this.regions;
37560 var north = rs["north"];
37561 var south = rs["south"];
37562 var west = rs["west"];
37563 var east = rs["east"];
37564 var center = rs["center"];
37565 //if(this.hideOnLayout){ // not supported anymore
37566 //c.el.setStyle("display", "none");
37568 if(north && north.isVisible()){
37569 var b = north.getBox();
37570 var m = north.getMargins();
37571 b.width = w - (m.left+m.right);
37574 centerY = b.height + b.y + m.bottom;
37575 centerH -= centerY;
37576 north.updateBox(this.safeBox(b));
37578 if(south && south.isVisible()){
37579 var b = south.getBox();
37580 var m = south.getMargins();
37581 b.width = w - (m.left+m.right);
37583 var totalHeight = (b.height + m.top + m.bottom);
37584 b.y = h - totalHeight + m.top;
37585 centerH -= totalHeight;
37586 south.updateBox(this.safeBox(b));
37588 if(west && west.isVisible()){
37589 var b = west.getBox();
37590 var m = west.getMargins();
37591 b.height = centerH - (m.top+m.bottom);
37593 b.y = centerY + m.top;
37594 var totalWidth = (b.width + m.left + m.right);
37595 centerX += totalWidth;
37596 centerW -= totalWidth;
37597 west.updateBox(this.safeBox(b));
37599 if(east && east.isVisible()){
37600 var b = east.getBox();
37601 var m = east.getMargins();
37602 b.height = centerH - (m.top+m.bottom);
37603 var totalWidth = (b.width + m.left + m.right);
37604 b.x = w - totalWidth + m.left;
37605 b.y = centerY + m.top;
37606 centerW -= totalWidth;
37607 east.updateBox(this.safeBox(b));
37610 var m = center.getMargins();
37612 x: centerX + m.left,
37613 y: centerY + m.top,
37614 width: centerW - (m.left+m.right),
37615 height: centerH - (m.top+m.bottom)
37617 //if(this.hideOnLayout){
37618 //center.el.setStyle("display", "block");
37620 center.updateBox(this.safeBox(centerBox));
37623 this.fireEvent("layout", this);
37627 safeBox : function(box){
37628 box.width = Math.max(0, box.width);
37629 box.height = Math.max(0, box.height);
37634 * Adds a ContentPanel (or subclass) to this layout.
37635 * @param {String} target The target region key (north, south, east, west or center).
37636 * @param {Roo.ContentPanel} panel The panel to add
37637 * @return {Roo.ContentPanel} The added panel
37639 add : function(target, panel){
37641 target = target.toLowerCase();
37642 return this.regions[target].add(panel);
37646 * Remove a ContentPanel (or subclass) to this layout.
37647 * @param {String} target The target region key (north, south, east, west or center).
37648 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37649 * @return {Roo.ContentPanel} The removed panel
37651 remove : function(target, panel){
37652 target = target.toLowerCase();
37653 return this.regions[target].remove(panel);
37657 * Searches all regions for a panel with the specified id
37658 * @param {String} panelId
37659 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37661 findPanel : function(panelId){
37662 var rs = this.regions;
37663 for(var target in rs){
37664 if(typeof rs[target] != "function"){
37665 var p = rs[target].getPanel(panelId);
37675 * Searches all regions for a panel with the specified id and activates (shows) it.
37676 * @param {String/ContentPanel} panelId The panels id or the panel itself
37677 * @return {Roo.ContentPanel} The shown panel or null
37679 showPanel : function(panelId) {
37680 var rs = this.regions;
37681 for(var target in rs){
37682 var r = rs[target];
37683 if(typeof r != "function"){
37684 if(r.hasPanel(panelId)){
37685 return r.showPanel(panelId);
37693 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37694 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37697 restoreState : function(provider){
37699 provider = Roo.state.Manager;
37701 var sm = new Roo.LayoutStateManager();
37702 sm.init(this, provider);
37708 * Adds a xtype elements to the layout.
37712 xtype : 'ContentPanel',
37719 xtype : 'NestedLayoutPanel',
37725 items : [ ... list of content panels or nested layout panels.. ]
37729 * @param {Object} cfg Xtype definition of item to add.
37731 addxtype : function(cfg)
37733 // basically accepts a pannel...
37734 // can accept a layout region..!?!?
37735 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37738 // theory? children can only be panels??
37740 //if (!cfg.xtype.match(/Panel$/)) {
37745 if (typeof(cfg.region) == 'undefined') {
37746 Roo.log("Failed to add Panel, region was not set");
37750 var region = cfg.region;
37756 xitems = cfg.items;
37761 if ( region == 'center') {
37762 Roo.log("Center: " + cfg.title);
37768 case 'Content': // ContentPanel (el, cfg)
37769 case 'Scroll': // ContentPanel (el, cfg)
37771 cfg.autoCreate = cfg.autoCreate || true;
37772 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37774 // var el = this.el.createChild();
37775 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37778 this.add(region, ret);
37782 case 'TreePanel': // our new panel!
37783 cfg.el = this.el.createChild();
37784 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37785 this.add(region, ret);
37790 // create a new Layout (which is a Border Layout...
37792 var clayout = cfg.layout;
37793 clayout.el = this.el.createChild();
37794 clayout.items = clayout.items || [];
37798 // replace this exitems with the clayout ones..
37799 xitems = clayout.items;
37801 // force background off if it's in center...
37802 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37803 cfg.background = false;
37805 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37808 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37809 //console.log('adding nested layout panel ' + cfg.toSource());
37810 this.add(region, ret);
37811 nb = {}; /// find first...
37816 // needs grid and region
37818 //var el = this.getRegion(region).el.createChild();
37820 *var el = this.el.createChild();
37821 // create the grid first...
37822 cfg.grid.container = el;
37823 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37826 if (region == 'center' && this.active ) {
37827 cfg.background = false;
37830 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37832 this.add(region, ret);
37834 if (cfg.background) {
37835 // render grid on panel activation (if panel background)
37836 ret.on('activate', function(gp) {
37837 if (!gp.grid.rendered) {
37838 // gp.grid.render(el);
37842 // cfg.grid.render(el);
37848 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37849 // it was the old xcomponent building that caused this before.
37850 // espeically if border is the top element in the tree.
37860 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37862 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37863 this.add(region, ret);
37867 throw "Can not add '" + cfg.xtype + "' to Border";
37873 this.beginUpdate();
37877 Roo.each(xitems, function(i) {
37878 region = nb && i.region ? i.region : false;
37880 var add = ret.addxtype(i);
37883 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37884 if (!i.background) {
37885 abn[region] = nb[region] ;
37892 // make the last non-background panel active..
37893 //if (nb) { Roo.log(abn); }
37896 for(var r in abn) {
37897 region = this.getRegion(r);
37899 // tried using nb[r], but it does not work..
37901 region.showPanel(abn[r]);
37912 factory : function(cfg)
37915 var validRegions = Roo.bootstrap.layout.Border.regions;
37917 var target = cfg.region;
37920 var r = Roo.bootstrap.layout;
37924 return new r.North(cfg);
37926 return new r.South(cfg);
37928 return new r.East(cfg);
37930 return new r.West(cfg);
37932 return new r.Center(cfg);
37934 throw 'Layout region "'+target+'" not supported.';
37941 * Ext JS Library 1.1.1
37942 * Copyright(c) 2006-2007, Ext JS, LLC.
37944 * Originally Released Under LGPL - original licence link has changed is not relivant.
37947 * <script type="text/javascript">
37951 * @class Roo.bootstrap.layout.Basic
37952 * @extends Roo.util.Observable
37953 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37954 * and does not have a titlebar, tabs or any other features. All it does is size and position
37955 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37956 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37957 * @cfg {string} region the region that it inhabits..
37958 * @cfg {bool} skipConfig skip config?
37962 Roo.bootstrap.layout.Basic = function(config){
37964 this.mgr = config.mgr;
37966 this.position = config.region;
37968 var skipConfig = config.skipConfig;
37972 * @scope Roo.BasicLayoutRegion
37976 * @event beforeremove
37977 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37978 * @param {Roo.LayoutRegion} this
37979 * @param {Roo.ContentPanel} panel The panel
37980 * @param {Object} e The cancel event object
37982 "beforeremove" : true,
37984 * @event invalidated
37985 * Fires when the layout for this region is changed.
37986 * @param {Roo.LayoutRegion} this
37988 "invalidated" : true,
37990 * @event visibilitychange
37991 * Fires when this region is shown or hidden
37992 * @param {Roo.LayoutRegion} this
37993 * @param {Boolean} visibility true or false
37995 "visibilitychange" : true,
37997 * @event paneladded
37998 * Fires when a panel is added.
37999 * @param {Roo.LayoutRegion} this
38000 * @param {Roo.ContentPanel} panel The panel
38002 "paneladded" : true,
38004 * @event panelremoved
38005 * Fires when a panel is removed.
38006 * @param {Roo.LayoutRegion} this
38007 * @param {Roo.ContentPanel} panel The panel
38009 "panelremoved" : true,
38011 * @event beforecollapse
38012 * Fires when this region before collapse.
38013 * @param {Roo.LayoutRegion} this
38015 "beforecollapse" : true,
38018 * Fires when this region is collapsed.
38019 * @param {Roo.LayoutRegion} this
38021 "collapsed" : true,
38024 * Fires when this region is expanded.
38025 * @param {Roo.LayoutRegion} this
38030 * Fires when this region is slid into view.
38031 * @param {Roo.LayoutRegion} this
38033 "slideshow" : true,
38036 * Fires when this region slides out of view.
38037 * @param {Roo.LayoutRegion} this
38039 "slidehide" : true,
38041 * @event panelactivated
38042 * Fires when a panel is activated.
38043 * @param {Roo.LayoutRegion} this
38044 * @param {Roo.ContentPanel} panel The activated panel
38046 "panelactivated" : true,
38049 * Fires when the user resizes this region.
38050 * @param {Roo.LayoutRegion} this
38051 * @param {Number} newSize The new size (width for east/west, height for north/south)
38055 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38056 this.panels = new Roo.util.MixedCollection();
38057 this.panels.getKey = this.getPanelId.createDelegate(this);
38059 this.activePanel = null;
38060 // ensure listeners are added...
38062 if (config.listeners || config.events) {
38063 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38064 listeners : config.listeners || {},
38065 events : config.events || {}
38069 if(skipConfig !== true){
38070 this.applyConfig(config);
38074 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38076 getPanelId : function(p){
38080 applyConfig : function(config){
38081 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38082 this.config = config;
38087 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38088 * the width, for horizontal (north, south) the height.
38089 * @param {Number} newSize The new width or height
38091 resizeTo : function(newSize){
38092 var el = this.el ? this.el :
38093 (this.activePanel ? this.activePanel.getEl() : null);
38095 switch(this.position){
38098 el.setWidth(newSize);
38099 this.fireEvent("resized", this, newSize);
38103 el.setHeight(newSize);
38104 this.fireEvent("resized", this, newSize);
38110 getBox : function(){
38111 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38114 getMargins : function(){
38115 return this.margins;
38118 updateBox : function(box){
38120 var el = this.activePanel.getEl();
38121 el.dom.style.left = box.x + "px";
38122 el.dom.style.top = box.y + "px";
38123 this.activePanel.setSize(box.width, box.height);
38127 * Returns the container element for this region.
38128 * @return {Roo.Element}
38130 getEl : function(){
38131 return this.activePanel;
38135 * Returns true if this region is currently visible.
38136 * @return {Boolean}
38138 isVisible : function(){
38139 return this.activePanel ? true : false;
38142 setActivePanel : function(panel){
38143 panel = this.getPanel(panel);
38144 if(this.activePanel && this.activePanel != panel){
38145 this.activePanel.setActiveState(false);
38146 this.activePanel.getEl().setLeftTop(-10000,-10000);
38148 this.activePanel = panel;
38149 panel.setActiveState(true);
38151 panel.setSize(this.box.width, this.box.height);
38153 this.fireEvent("panelactivated", this, panel);
38154 this.fireEvent("invalidated");
38158 * Show the specified panel.
38159 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38160 * @return {Roo.ContentPanel} The shown panel or null
38162 showPanel : function(panel){
38163 panel = this.getPanel(panel);
38165 this.setActivePanel(panel);
38171 * Get the active panel for this region.
38172 * @return {Roo.ContentPanel} The active panel or null
38174 getActivePanel : function(){
38175 return this.activePanel;
38179 * Add the passed ContentPanel(s)
38180 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38181 * @return {Roo.ContentPanel} The panel added (if only one was added)
38183 add : function(panel){
38184 if(arguments.length > 1){
38185 for(var i = 0, len = arguments.length; i < len; i++) {
38186 this.add(arguments[i]);
38190 if(this.hasPanel(panel)){
38191 this.showPanel(panel);
38194 var el = panel.getEl();
38195 if(el.dom.parentNode != this.mgr.el.dom){
38196 this.mgr.el.dom.appendChild(el.dom);
38198 if(panel.setRegion){
38199 panel.setRegion(this);
38201 this.panels.add(panel);
38202 el.setStyle("position", "absolute");
38203 if(!panel.background){
38204 this.setActivePanel(panel);
38205 if(this.config.initialSize && this.panels.getCount()==1){
38206 this.resizeTo(this.config.initialSize);
38209 this.fireEvent("paneladded", this, panel);
38214 * Returns true if the panel is in this region.
38215 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38216 * @return {Boolean}
38218 hasPanel : function(panel){
38219 if(typeof panel == "object"){ // must be panel obj
38220 panel = panel.getId();
38222 return this.getPanel(panel) ? true : false;
38226 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38227 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38228 * @param {Boolean} preservePanel Overrides the config preservePanel option
38229 * @return {Roo.ContentPanel} The panel that was removed
38231 remove : function(panel, preservePanel){
38232 panel = this.getPanel(panel);
38237 this.fireEvent("beforeremove", this, panel, e);
38238 if(e.cancel === true){
38241 var panelId = panel.getId();
38242 this.panels.removeKey(panelId);
38247 * Returns the panel specified or null if it's not in this region.
38248 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38249 * @return {Roo.ContentPanel}
38251 getPanel : function(id){
38252 if(typeof id == "object"){ // must be panel obj
38255 return this.panels.get(id);
38259 * Returns this regions position (north/south/east/west/center).
38262 getPosition: function(){
38263 return this.position;
38267 * Ext JS Library 1.1.1
38268 * Copyright(c) 2006-2007, Ext JS, LLC.
38270 * Originally Released Under LGPL - original licence link has changed is not relivant.
38273 * <script type="text/javascript">
38277 * @class Roo.bootstrap.layout.Region
38278 * @extends Roo.bootstrap.layout.Basic
38279 * This class represents a region in a layout manager.
38281 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38282 * @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})
38283 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38284 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38285 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38286 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38287 * @cfg {String} title The title for the region (overrides panel titles)
38288 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38289 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38290 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38291 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38292 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38293 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38294 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38295 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38296 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38297 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38299 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38300 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38301 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38302 * @cfg {Number} width For East/West panels
38303 * @cfg {Number} height For North/South panels
38304 * @cfg {Boolean} split To show the splitter
38305 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38307 * @cfg {string} cls Extra CSS classes to add to region
38309 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38310 * @cfg {string} region the region that it inhabits..
38313 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38314 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38316 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38317 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38318 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38320 Roo.bootstrap.layout.Region = function(config)
38322 this.applyConfig(config);
38324 var mgr = config.mgr;
38325 var pos = config.region;
38326 config.skipConfig = true;
38327 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38330 this.onRender(mgr.el);
38333 this.visible = true;
38334 this.collapsed = false;
38335 this.unrendered_panels = [];
38338 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38340 position: '', // set by wrapper (eg. north/south etc..)
38341 unrendered_panels : null, // unrendered panels.
38343 tabPosition : false,
38345 mgr: false, // points to 'Border'
38348 createBody : function(){
38349 /** This region's body element
38350 * @type Roo.Element */
38351 this.bodyEl = this.el.createChild({
38353 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38357 onRender: function(ctr, pos)
38359 var dh = Roo.DomHelper;
38360 /** This region's container element
38361 * @type Roo.Element */
38362 this.el = dh.append(ctr.dom, {
38364 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38366 /** This region's title element
38367 * @type Roo.Element */
38369 this.titleEl = dh.append(this.el.dom, {
38371 unselectable: "on",
38372 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38374 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38375 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38379 this.titleEl.enableDisplayMode();
38380 /** This region's title text element
38381 * @type HTMLElement */
38382 this.titleTextEl = this.titleEl.dom.firstChild;
38383 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38385 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38386 this.closeBtn.enableDisplayMode();
38387 this.closeBtn.on("click", this.closeClicked, this);
38388 this.closeBtn.hide();
38390 this.createBody(this.config);
38391 if(this.config.hideWhenEmpty){
38393 this.on("paneladded", this.validateVisibility, this);
38394 this.on("panelremoved", this.validateVisibility, this);
38396 if(this.autoScroll){
38397 this.bodyEl.setStyle("overflow", "auto");
38399 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38401 //if(c.titlebar !== false){
38402 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38403 this.titleEl.hide();
38405 this.titleEl.show();
38406 if(this.config.title){
38407 this.titleTextEl.innerHTML = this.config.title;
38411 if(this.config.collapsed){
38412 this.collapse(true);
38414 if(this.config.hidden){
38418 if (this.unrendered_panels && this.unrendered_panels.length) {
38419 for (var i =0;i< this.unrendered_panels.length; i++) {
38420 this.add(this.unrendered_panels[i]);
38422 this.unrendered_panels = null;
38428 applyConfig : function(c)
38431 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38432 var dh = Roo.DomHelper;
38433 if(c.titlebar !== false){
38434 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38435 this.collapseBtn.on("click", this.collapse, this);
38436 this.collapseBtn.enableDisplayMode();
38438 if(c.showPin === true || this.showPin){
38439 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38440 this.stickBtn.enableDisplayMode();
38441 this.stickBtn.on("click", this.expand, this);
38442 this.stickBtn.hide();
38447 /** This region's collapsed element
38448 * @type Roo.Element */
38451 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38452 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38455 if(c.floatable !== false){
38456 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38457 this.collapsedEl.on("click", this.collapseClick, this);
38460 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38461 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38462 id: "message", unselectable: "on", style:{"float":"left"}});
38463 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38465 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38466 this.expandBtn.on("click", this.expand, this);
38470 if(this.collapseBtn){
38471 this.collapseBtn.setVisible(c.collapsible == true);
38474 this.cmargins = c.cmargins || this.cmargins ||
38475 (this.position == "west" || this.position == "east" ?
38476 {top: 0, left: 2, right:2, bottom: 0} :
38477 {top: 2, left: 0, right:0, bottom: 2});
38479 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38482 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38484 this.autoScroll = c.autoScroll || false;
38489 this.duration = c.duration || .30;
38490 this.slideDuration = c.slideDuration || .45;
38495 * Returns true if this region is currently visible.
38496 * @return {Boolean}
38498 isVisible : function(){
38499 return this.visible;
38503 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38504 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38506 //setCollapsedTitle : function(title){
38507 // title = title || " ";
38508 // if(this.collapsedTitleTextEl){
38509 // this.collapsedTitleTextEl.innerHTML = title;
38513 getBox : function(){
38515 // if(!this.collapsed){
38516 b = this.el.getBox(false, true);
38518 // b = this.collapsedEl.getBox(false, true);
38523 getMargins : function(){
38524 return this.margins;
38525 //return this.collapsed ? this.cmargins : this.margins;
38528 highlight : function(){
38529 this.el.addClass("x-layout-panel-dragover");
38532 unhighlight : function(){
38533 this.el.removeClass("x-layout-panel-dragover");
38536 updateBox : function(box)
38538 if (!this.bodyEl) {
38539 return; // not rendered yet..
38543 if(!this.collapsed){
38544 this.el.dom.style.left = box.x + "px";
38545 this.el.dom.style.top = box.y + "px";
38546 this.updateBody(box.width, box.height);
38548 this.collapsedEl.dom.style.left = box.x + "px";
38549 this.collapsedEl.dom.style.top = box.y + "px";
38550 this.collapsedEl.setSize(box.width, box.height);
38553 this.tabs.autoSizeTabs();
38557 updateBody : function(w, h)
38560 this.el.setWidth(w);
38561 w -= this.el.getBorderWidth("rl");
38562 if(this.config.adjustments){
38563 w += this.config.adjustments[0];
38566 if(h !== null && h > 0){
38567 this.el.setHeight(h);
38568 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38569 h -= this.el.getBorderWidth("tb");
38570 if(this.config.adjustments){
38571 h += this.config.adjustments[1];
38573 this.bodyEl.setHeight(h);
38575 h = this.tabs.syncHeight(h);
38578 if(this.panelSize){
38579 w = w !== null ? w : this.panelSize.width;
38580 h = h !== null ? h : this.panelSize.height;
38582 if(this.activePanel){
38583 var el = this.activePanel.getEl();
38584 w = w !== null ? w : el.getWidth();
38585 h = h !== null ? h : el.getHeight();
38586 this.panelSize = {width: w, height: h};
38587 this.activePanel.setSize(w, h);
38589 if(Roo.isIE && this.tabs){
38590 this.tabs.el.repaint();
38595 * Returns the container element for this region.
38596 * @return {Roo.Element}
38598 getEl : function(){
38603 * Hides this region.
38606 //if(!this.collapsed){
38607 this.el.dom.style.left = "-2000px";
38610 // this.collapsedEl.dom.style.left = "-2000px";
38611 // this.collapsedEl.hide();
38613 this.visible = false;
38614 this.fireEvent("visibilitychange", this, false);
38618 * Shows this region if it was previously hidden.
38621 //if(!this.collapsed){
38624 // this.collapsedEl.show();
38626 this.visible = true;
38627 this.fireEvent("visibilitychange", this, true);
38630 closeClicked : function(){
38631 if(this.activePanel){
38632 this.remove(this.activePanel);
38636 collapseClick : function(e){
38638 e.stopPropagation();
38641 e.stopPropagation();
38647 * Collapses this region.
38648 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38651 collapse : function(skipAnim, skipCheck = false){
38652 if(this.collapsed) {
38656 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38658 this.collapsed = true;
38660 this.split.el.hide();
38662 if(this.config.animate && skipAnim !== true){
38663 this.fireEvent("invalidated", this);
38664 this.animateCollapse();
38666 this.el.setLocation(-20000,-20000);
38668 this.collapsedEl.show();
38669 this.fireEvent("collapsed", this);
38670 this.fireEvent("invalidated", this);
38676 animateCollapse : function(){
38681 * Expands this region if it was previously collapsed.
38682 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38683 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38686 expand : function(e, skipAnim){
38688 e.stopPropagation();
38690 if(!this.collapsed || this.el.hasActiveFx()) {
38694 this.afterSlideIn();
38697 this.collapsed = false;
38698 if(this.config.animate && skipAnim !== true){
38699 this.animateExpand();
38703 this.split.el.show();
38705 this.collapsedEl.setLocation(-2000,-2000);
38706 this.collapsedEl.hide();
38707 this.fireEvent("invalidated", this);
38708 this.fireEvent("expanded", this);
38712 animateExpand : function(){
38716 initTabs : function()
38718 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38720 var ts = new Roo.bootstrap.panel.Tabs({
38721 el: this.bodyEl.dom,
38723 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38724 disableTooltips: this.config.disableTabTips,
38725 toolbar : this.config.toolbar
38728 if(this.config.hideTabs){
38729 ts.stripWrap.setDisplayed(false);
38732 ts.resizeTabs = this.config.resizeTabs === true;
38733 ts.minTabWidth = this.config.minTabWidth || 40;
38734 ts.maxTabWidth = this.config.maxTabWidth || 250;
38735 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38736 ts.monitorResize = false;
38737 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38738 ts.bodyEl.addClass('roo-layout-tabs-body');
38739 this.panels.each(this.initPanelAsTab, this);
38742 initPanelAsTab : function(panel){
38743 var ti = this.tabs.addTab(
38747 this.config.closeOnTab && panel.isClosable(),
38750 if(panel.tabTip !== undefined){
38751 ti.setTooltip(panel.tabTip);
38753 ti.on("activate", function(){
38754 this.setActivePanel(panel);
38757 if(this.config.closeOnTab){
38758 ti.on("beforeclose", function(t, e){
38760 this.remove(panel);
38764 panel.tabItem = ti;
38769 updatePanelTitle : function(panel, title)
38771 if(this.activePanel == panel){
38772 this.updateTitle(title);
38775 var ti = this.tabs.getTab(panel.getEl().id);
38777 if(panel.tabTip !== undefined){
38778 ti.setTooltip(panel.tabTip);
38783 updateTitle : function(title){
38784 if(this.titleTextEl && !this.config.title){
38785 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38789 setActivePanel : function(panel)
38791 panel = this.getPanel(panel);
38792 if(this.activePanel && this.activePanel != panel){
38793 if(this.activePanel.setActiveState(false) === false){
38797 this.activePanel = panel;
38798 panel.setActiveState(true);
38799 if(this.panelSize){
38800 panel.setSize(this.panelSize.width, this.panelSize.height);
38803 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38805 this.updateTitle(panel.getTitle());
38807 this.fireEvent("invalidated", this);
38809 this.fireEvent("panelactivated", this, panel);
38813 * Shows the specified panel.
38814 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38815 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38817 showPanel : function(panel)
38819 panel = this.getPanel(panel);
38822 var tab = this.tabs.getTab(panel.getEl().id);
38823 if(tab.isHidden()){
38824 this.tabs.unhideTab(tab.id);
38828 this.setActivePanel(panel);
38835 * Get the active panel for this region.
38836 * @return {Roo.ContentPanel} The active panel or null
38838 getActivePanel : function(){
38839 return this.activePanel;
38842 validateVisibility : function(){
38843 if(this.panels.getCount() < 1){
38844 this.updateTitle(" ");
38845 this.closeBtn.hide();
38848 if(!this.isVisible()){
38855 * Adds the passed ContentPanel(s) to this region.
38856 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38857 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38859 add : function(panel)
38861 if(arguments.length > 1){
38862 for(var i = 0, len = arguments.length; i < len; i++) {
38863 this.add(arguments[i]);
38868 // if we have not been rendered yet, then we can not really do much of this..
38869 if (!this.bodyEl) {
38870 this.unrendered_panels.push(panel);
38877 if(this.hasPanel(panel)){
38878 this.showPanel(panel);
38881 panel.setRegion(this);
38882 this.panels.add(panel);
38883 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38884 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38885 // and hide them... ???
38886 this.bodyEl.dom.appendChild(panel.getEl().dom);
38887 if(panel.background !== true){
38888 this.setActivePanel(panel);
38890 this.fireEvent("paneladded", this, panel);
38897 this.initPanelAsTab(panel);
38901 if(panel.background !== true){
38902 this.tabs.activate(panel.getEl().id);
38904 this.fireEvent("paneladded", this, panel);
38909 * Hides the tab for the specified panel.
38910 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38912 hidePanel : function(panel){
38913 if(this.tabs && (panel = this.getPanel(panel))){
38914 this.tabs.hideTab(panel.getEl().id);
38919 * Unhides the tab for a previously hidden panel.
38920 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38922 unhidePanel : function(panel){
38923 if(this.tabs && (panel = this.getPanel(panel))){
38924 this.tabs.unhideTab(panel.getEl().id);
38928 clearPanels : function(){
38929 while(this.panels.getCount() > 0){
38930 this.remove(this.panels.first());
38935 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38936 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38937 * @param {Boolean} preservePanel Overrides the config preservePanel option
38938 * @return {Roo.ContentPanel} The panel that was removed
38940 remove : function(panel, preservePanel)
38942 panel = this.getPanel(panel);
38947 this.fireEvent("beforeremove", this, panel, e);
38948 if(e.cancel === true){
38951 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38952 var panelId = panel.getId();
38953 this.panels.removeKey(panelId);
38955 document.body.appendChild(panel.getEl().dom);
38958 this.tabs.removeTab(panel.getEl().id);
38959 }else if (!preservePanel){
38960 this.bodyEl.dom.removeChild(panel.getEl().dom);
38962 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38963 var p = this.panels.first();
38964 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38965 tempEl.appendChild(p.getEl().dom);
38966 this.bodyEl.update("");
38967 this.bodyEl.dom.appendChild(p.getEl().dom);
38969 this.updateTitle(p.getTitle());
38971 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38972 this.setActivePanel(p);
38974 panel.setRegion(null);
38975 if(this.activePanel == panel){
38976 this.activePanel = null;
38978 if(this.config.autoDestroy !== false && preservePanel !== true){
38979 try{panel.destroy();}catch(e){}
38981 this.fireEvent("panelremoved", this, panel);
38986 * Returns the TabPanel component used by this region
38987 * @return {Roo.TabPanel}
38989 getTabs : function(){
38993 createTool : function(parentEl, className){
38994 var btn = Roo.DomHelper.append(parentEl, {
38996 cls: "x-layout-tools-button",
38999 cls: "roo-layout-tools-button-inner " + className,
39003 btn.addClassOnOver("roo-layout-tools-button-over");
39008 * Ext JS Library 1.1.1
39009 * Copyright(c) 2006-2007, Ext JS, LLC.
39011 * Originally Released Under LGPL - original licence link has changed is not relivant.
39014 * <script type="text/javascript">
39020 * @class Roo.SplitLayoutRegion
39021 * @extends Roo.LayoutRegion
39022 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39024 Roo.bootstrap.layout.Split = function(config){
39025 this.cursor = config.cursor;
39026 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39029 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39031 splitTip : "Drag to resize.",
39032 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39033 useSplitTips : false,
39035 applyConfig : function(config){
39036 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39039 onRender : function(ctr,pos) {
39041 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39042 if(!this.config.split){
39047 var splitEl = Roo.DomHelper.append(ctr.dom, {
39049 id: this.el.id + "-split",
39050 cls: "roo-layout-split roo-layout-split-"+this.position,
39053 /** The SplitBar for this region
39054 * @type Roo.SplitBar */
39055 // does not exist yet...
39056 Roo.log([this.position, this.orientation]);
39058 this.split = new Roo.bootstrap.SplitBar({
39059 dragElement : splitEl,
39060 resizingElement: this.el,
39061 orientation : this.orientation
39064 this.split.on("moved", this.onSplitMove, this);
39065 this.split.useShim = this.config.useShim === true;
39066 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39067 if(this.useSplitTips){
39068 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39070 //if(config.collapsible){
39071 // this.split.el.on("dblclick", this.collapse, this);
39074 if(typeof this.config.minSize != "undefined"){
39075 this.split.minSize = this.config.minSize;
39077 if(typeof this.config.maxSize != "undefined"){
39078 this.split.maxSize = this.config.maxSize;
39080 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39081 this.hideSplitter();
39086 getHMaxSize : function(){
39087 var cmax = this.config.maxSize || 10000;
39088 var center = this.mgr.getRegion("center");
39089 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39092 getVMaxSize : function(){
39093 var cmax = this.config.maxSize || 10000;
39094 var center = this.mgr.getRegion("center");
39095 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39098 onSplitMove : function(split, newSize){
39099 this.fireEvent("resized", this, newSize);
39103 * Returns the {@link Roo.SplitBar} for this region.
39104 * @return {Roo.SplitBar}
39106 getSplitBar : function(){
39111 this.hideSplitter();
39112 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39115 hideSplitter : function(){
39117 this.split.el.setLocation(-2000,-2000);
39118 this.split.el.hide();
39124 this.split.el.show();
39126 Roo.bootstrap.layout.Split.superclass.show.call(this);
39129 beforeSlide: function(){
39130 if(Roo.isGecko){// firefox overflow auto bug workaround
39131 this.bodyEl.clip();
39133 this.tabs.bodyEl.clip();
39135 if(this.activePanel){
39136 this.activePanel.getEl().clip();
39138 if(this.activePanel.beforeSlide){
39139 this.activePanel.beforeSlide();
39145 afterSlide : function(){
39146 if(Roo.isGecko){// firefox overflow auto bug workaround
39147 this.bodyEl.unclip();
39149 this.tabs.bodyEl.unclip();
39151 if(this.activePanel){
39152 this.activePanel.getEl().unclip();
39153 if(this.activePanel.afterSlide){
39154 this.activePanel.afterSlide();
39160 initAutoHide : function(){
39161 if(this.autoHide !== false){
39162 if(!this.autoHideHd){
39163 var st = new Roo.util.DelayedTask(this.slideIn, this);
39164 this.autoHideHd = {
39165 "mouseout": function(e){
39166 if(!e.within(this.el, true)){
39170 "mouseover" : function(e){
39176 this.el.on(this.autoHideHd);
39180 clearAutoHide : function(){
39181 if(this.autoHide !== false){
39182 this.el.un("mouseout", this.autoHideHd.mouseout);
39183 this.el.un("mouseover", this.autoHideHd.mouseover);
39187 clearMonitor : function(){
39188 Roo.get(document).un("click", this.slideInIf, this);
39191 // these names are backwards but not changed for compat
39192 slideOut : function(){
39193 if(this.isSlid || this.el.hasActiveFx()){
39196 this.isSlid = true;
39197 if(this.collapseBtn){
39198 this.collapseBtn.hide();
39200 this.closeBtnState = this.closeBtn.getStyle('display');
39201 this.closeBtn.hide();
39203 this.stickBtn.show();
39206 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39207 this.beforeSlide();
39208 this.el.setStyle("z-index", 10001);
39209 this.el.slideIn(this.getSlideAnchor(), {
39210 callback: function(){
39212 this.initAutoHide();
39213 Roo.get(document).on("click", this.slideInIf, this);
39214 this.fireEvent("slideshow", this);
39221 afterSlideIn : function(){
39222 this.clearAutoHide();
39223 this.isSlid = false;
39224 this.clearMonitor();
39225 this.el.setStyle("z-index", "");
39226 if(this.collapseBtn){
39227 this.collapseBtn.show();
39229 this.closeBtn.setStyle('display', this.closeBtnState);
39231 this.stickBtn.hide();
39233 this.fireEvent("slidehide", this);
39236 slideIn : function(cb){
39237 if(!this.isSlid || this.el.hasActiveFx()){
39241 this.isSlid = false;
39242 this.beforeSlide();
39243 this.el.slideOut(this.getSlideAnchor(), {
39244 callback: function(){
39245 this.el.setLeftTop(-10000, -10000);
39247 this.afterSlideIn();
39255 slideInIf : function(e){
39256 if(!e.within(this.el)){
39261 animateCollapse : function(){
39262 this.beforeSlide();
39263 this.el.setStyle("z-index", 20000);
39264 var anchor = this.getSlideAnchor();
39265 this.el.slideOut(anchor, {
39266 callback : function(){
39267 this.el.setStyle("z-index", "");
39268 this.collapsedEl.slideIn(anchor, {duration:.3});
39270 this.el.setLocation(-10000,-10000);
39272 this.fireEvent("collapsed", this);
39279 animateExpand : function(){
39280 this.beforeSlide();
39281 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39282 this.el.setStyle("z-index", 20000);
39283 this.collapsedEl.hide({
39286 this.el.slideIn(this.getSlideAnchor(), {
39287 callback : function(){
39288 this.el.setStyle("z-index", "");
39291 this.split.el.show();
39293 this.fireEvent("invalidated", this);
39294 this.fireEvent("expanded", this);
39322 getAnchor : function(){
39323 return this.anchors[this.position];
39326 getCollapseAnchor : function(){
39327 return this.canchors[this.position];
39330 getSlideAnchor : function(){
39331 return this.sanchors[this.position];
39334 getAlignAdj : function(){
39335 var cm = this.cmargins;
39336 switch(this.position){
39352 getExpandAdj : function(){
39353 var c = this.collapsedEl, cm = this.cmargins;
39354 switch(this.position){
39356 return [-(cm.right+c.getWidth()+cm.left), 0];
39359 return [cm.right+c.getWidth()+cm.left, 0];
39362 return [0, -(cm.top+cm.bottom+c.getHeight())];
39365 return [0, cm.top+cm.bottom+c.getHeight()];
39371 * Ext JS Library 1.1.1
39372 * Copyright(c) 2006-2007, Ext JS, LLC.
39374 * Originally Released Under LGPL - original licence link has changed is not relivant.
39377 * <script type="text/javascript">
39380 * These classes are private internal classes
39382 Roo.bootstrap.layout.Center = function(config){
39383 config.region = "center";
39384 Roo.bootstrap.layout.Region.call(this, config);
39385 this.visible = true;
39386 this.minWidth = config.minWidth || 20;
39387 this.minHeight = config.minHeight || 20;
39390 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39392 // center panel can't be hidden
39396 // center panel can't be hidden
39399 getMinWidth: function(){
39400 return this.minWidth;
39403 getMinHeight: function(){
39404 return this.minHeight;
39418 Roo.bootstrap.layout.North = function(config)
39420 config.region = 'north';
39421 config.cursor = 'n-resize';
39423 Roo.bootstrap.layout.Split.call(this, config);
39427 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39428 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39429 this.split.el.addClass("roo-layout-split-v");
39431 //var size = config.initialSize || config.height;
39432 //if(this.el && typeof size != "undefined"){
39433 // this.el.setHeight(size);
39436 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39438 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39441 onRender : function(ctr, pos)
39443 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39444 var size = this.config.initialSize || this.config.height;
39445 if(this.el && typeof size != "undefined"){
39446 this.el.setHeight(size);
39451 getBox : function(){
39452 if(this.collapsed){
39453 return this.collapsedEl.getBox();
39455 var box = this.el.getBox();
39457 box.height += this.split.el.getHeight();
39462 updateBox : function(box){
39463 if(this.split && !this.collapsed){
39464 box.height -= this.split.el.getHeight();
39465 this.split.el.setLeft(box.x);
39466 this.split.el.setTop(box.y+box.height);
39467 this.split.el.setWidth(box.width);
39469 if(this.collapsed){
39470 this.updateBody(box.width, null);
39472 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39480 Roo.bootstrap.layout.South = function(config){
39481 config.region = 'south';
39482 config.cursor = 's-resize';
39483 Roo.bootstrap.layout.Split.call(this, config);
39485 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39486 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39487 this.split.el.addClass("roo-layout-split-v");
39492 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39493 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39495 onRender : function(ctr, pos)
39497 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39498 var size = this.config.initialSize || this.config.height;
39499 if(this.el && typeof size != "undefined"){
39500 this.el.setHeight(size);
39505 getBox : function(){
39506 if(this.collapsed){
39507 return this.collapsedEl.getBox();
39509 var box = this.el.getBox();
39511 var sh = this.split.el.getHeight();
39518 updateBox : function(box){
39519 if(this.split && !this.collapsed){
39520 var sh = this.split.el.getHeight();
39523 this.split.el.setLeft(box.x);
39524 this.split.el.setTop(box.y-sh);
39525 this.split.el.setWidth(box.width);
39527 if(this.collapsed){
39528 this.updateBody(box.width, null);
39530 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39534 Roo.bootstrap.layout.East = function(config){
39535 config.region = "east";
39536 config.cursor = "e-resize";
39537 Roo.bootstrap.layout.Split.call(this, config);
39539 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39540 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39541 this.split.el.addClass("roo-layout-split-h");
39545 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39546 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39548 onRender : function(ctr, pos)
39550 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39551 var size = this.config.initialSize || this.config.width;
39552 if(this.el && typeof size != "undefined"){
39553 this.el.setWidth(size);
39558 getBox : function(){
39559 if(this.collapsed){
39560 return this.collapsedEl.getBox();
39562 var box = this.el.getBox();
39564 var sw = this.split.el.getWidth();
39571 updateBox : function(box){
39572 if(this.split && !this.collapsed){
39573 var sw = this.split.el.getWidth();
39575 this.split.el.setLeft(box.x);
39576 this.split.el.setTop(box.y);
39577 this.split.el.setHeight(box.height);
39580 if(this.collapsed){
39581 this.updateBody(null, box.height);
39583 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39587 Roo.bootstrap.layout.West = function(config){
39588 config.region = "west";
39589 config.cursor = "w-resize";
39591 Roo.bootstrap.layout.Split.call(this, config);
39593 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39594 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39595 this.split.el.addClass("roo-layout-split-h");
39599 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39600 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39602 onRender: function(ctr, pos)
39604 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39605 var size = this.config.initialSize || this.config.width;
39606 if(typeof size != "undefined"){
39607 this.el.setWidth(size);
39611 getBox : function(){
39612 if(this.collapsed){
39613 return this.collapsedEl.getBox();
39615 var box = this.el.getBox();
39616 if (box.width == 0) {
39617 box.width = this.config.width; // kludge?
39620 box.width += this.split.el.getWidth();
39625 updateBox : function(box){
39626 if(this.split && !this.collapsed){
39627 var sw = this.split.el.getWidth();
39629 this.split.el.setLeft(box.x+box.width);
39630 this.split.el.setTop(box.y);
39631 this.split.el.setHeight(box.height);
39633 if(this.collapsed){
39634 this.updateBody(null, box.height);
39636 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39638 });Roo.namespace("Roo.bootstrap.panel");/*
39640 * Ext JS Library 1.1.1
39641 * Copyright(c) 2006-2007, Ext JS, LLC.
39643 * Originally Released Under LGPL - original licence link has changed is not relivant.
39646 * <script type="text/javascript">
39649 * @class Roo.ContentPanel
39650 * @extends Roo.util.Observable
39651 * A basic ContentPanel element.
39652 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39653 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39654 * @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
39655 * @cfg {Boolean} closable True if the panel can be closed/removed
39656 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39657 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39658 * @cfg {Toolbar} toolbar A toolbar for this panel
39659 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39660 * @cfg {String} title The title for this panel
39661 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39662 * @cfg {String} url Calls {@link #setUrl} with this value
39663 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39664 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39665 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39666 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39667 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39668 * @cfg {Boolean} badges render the badges
39669 * @cfg {String} cls extra classes to use
39670 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39673 * Create a new ContentPanel.
39674 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39675 * @param {String/Object} config A string to set only the title or a config object
39676 * @param {String} content (optional) Set the HTML content for this panel
39677 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39679 Roo.bootstrap.panel.Content = function( config){
39681 this.tpl = config.tpl || false;
39683 var el = config.el;
39684 var content = config.content;
39686 if(config.autoCreate){ // xtype is available if this is called from factory
39689 this.el = Roo.get(el);
39690 if(!this.el && config && config.autoCreate){
39691 if(typeof config.autoCreate == "object"){
39692 if(!config.autoCreate.id){
39693 config.autoCreate.id = config.id||el;
39695 this.el = Roo.DomHelper.append(document.body,
39696 config.autoCreate, true);
39700 cls: (config.cls || '') +
39701 (config.background ? ' bg-' + config.background : '') +
39702 " roo-layout-inactive-content",
39705 if (config.iframe) {
39709 style : 'border: 0px',
39710 src : 'about:blank'
39716 elcfg.html = config.html;
39720 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39721 if (config.iframe) {
39722 this.iframeEl = this.el.select('iframe',true).first();
39727 this.closable = false;
39728 this.loaded = false;
39729 this.active = false;
39732 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39734 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39736 this.wrapEl = this.el; //this.el.wrap();
39738 if (config.toolbar.items) {
39739 ti = config.toolbar.items ;
39740 delete config.toolbar.items ;
39744 this.toolbar.render(this.wrapEl, 'before');
39745 for(var i =0;i < ti.length;i++) {
39746 // Roo.log(['add child', items[i]]);
39747 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39749 this.toolbar.items = nitems;
39750 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39751 delete config.toolbar;
39755 // xtype created footer. - not sure if will work as we normally have to render first..
39756 if (this.footer && !this.footer.el && this.footer.xtype) {
39757 if (!this.wrapEl) {
39758 this.wrapEl = this.el.wrap();
39761 this.footer.container = this.wrapEl.createChild();
39763 this.footer = Roo.factory(this.footer, Roo);
39768 if(typeof config == "string"){
39769 this.title = config;
39771 Roo.apply(this, config);
39775 this.resizeEl = Roo.get(this.resizeEl, true);
39777 this.resizeEl = this.el;
39779 // handle view.xtype
39787 * Fires when this panel is activated.
39788 * @param {Roo.ContentPanel} this
39792 * @event deactivate
39793 * Fires when this panel is activated.
39794 * @param {Roo.ContentPanel} this
39796 "deactivate" : true,
39800 * Fires when this panel is resized if fitToFrame is true.
39801 * @param {Roo.ContentPanel} this
39802 * @param {Number} width The width after any component adjustments
39803 * @param {Number} height The height after any component adjustments
39809 * Fires when this tab is created
39810 * @param {Roo.ContentPanel} this
39821 if(this.autoScroll && !this.iframe){
39822 this.resizeEl.setStyle("overflow", "auto");
39824 // fix randome scrolling
39825 //this.el.on('scroll', function() {
39826 // Roo.log('fix random scolling');
39827 // this.scrollTo('top',0);
39830 content = content || this.content;
39832 this.setContent(content);
39834 if(config && config.url){
39835 this.setUrl(this.url, this.params, this.loadOnce);
39840 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39842 if (this.view && typeof(this.view.xtype) != 'undefined') {
39843 this.view.el = this.el.appendChild(document.createElement("div"));
39844 this.view = Roo.factory(this.view);
39845 this.view.render && this.view.render(false, '');
39849 this.fireEvent('render', this);
39852 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39862 setRegion : function(region){
39863 this.region = region;
39864 this.setActiveClass(region && !this.background);
39868 setActiveClass: function(state)
39871 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39872 this.el.setStyle('position','relative');
39874 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39875 this.el.setStyle('position', 'absolute');
39880 * Returns the toolbar for this Panel if one was configured.
39881 * @return {Roo.Toolbar}
39883 getToolbar : function(){
39884 return this.toolbar;
39887 setActiveState : function(active)
39889 this.active = active;
39890 this.setActiveClass(active);
39892 if(this.fireEvent("deactivate", this) === false){
39897 this.fireEvent("activate", this);
39901 * Updates this panel's element (not for iframe)
39902 * @param {String} content The new content
39903 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39905 setContent : function(content, loadScripts){
39910 this.el.update(content, loadScripts);
39913 ignoreResize : function(w, h){
39914 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39917 this.lastSize = {width: w, height: h};
39922 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39923 * @return {Roo.UpdateManager} The UpdateManager
39925 getUpdateManager : function(){
39929 return this.el.getUpdateManager();
39932 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39933 * Does not work with IFRAME contents
39934 * @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:
39937 url: "your-url.php",
39938 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39939 callback: yourFunction,
39940 scope: yourObject, //(optional scope)
39943 text: "Loading...",
39949 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39950 * 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.
39951 * @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}
39952 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39953 * @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.
39954 * @return {Roo.ContentPanel} this
39962 var um = this.el.getUpdateManager();
39963 um.update.apply(um, arguments);
39969 * 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.
39970 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39971 * @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)
39972 * @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)
39973 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39975 setUrl : function(url, params, loadOnce){
39977 this.iframeEl.dom.src = url;
39981 if(this.refreshDelegate){
39982 this.removeListener("activate", this.refreshDelegate);
39984 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39985 this.on("activate", this.refreshDelegate);
39986 return this.el.getUpdateManager();
39989 _handleRefresh : function(url, params, loadOnce){
39990 if(!loadOnce || !this.loaded){
39991 var updater = this.el.getUpdateManager();
39992 updater.update(url, params, this._setLoaded.createDelegate(this));
39996 _setLoaded : function(){
39997 this.loaded = true;
40001 * Returns this panel's id
40004 getId : function(){
40009 * Returns this panel's element - used by regiosn to add.
40010 * @return {Roo.Element}
40012 getEl : function(){
40013 return this.wrapEl || this.el;
40018 adjustForComponents : function(width, height)
40020 //Roo.log('adjustForComponents ');
40021 if(this.resizeEl != this.el){
40022 width -= this.el.getFrameWidth('lr');
40023 height -= this.el.getFrameWidth('tb');
40026 var te = this.toolbar.getEl();
40027 te.setWidth(width);
40028 height -= te.getHeight();
40031 var te = this.footer.getEl();
40032 te.setWidth(width);
40033 height -= te.getHeight();
40037 if(this.adjustments){
40038 width += this.adjustments[0];
40039 height += this.adjustments[1];
40041 return {"width": width, "height": height};
40044 setSize : function(width, height){
40045 if(this.fitToFrame && !this.ignoreResize(width, height)){
40046 if(this.fitContainer && this.resizeEl != this.el){
40047 this.el.setSize(width, height);
40049 var size = this.adjustForComponents(width, height);
40051 this.iframeEl.setSize(width,height);
40054 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40055 this.fireEvent('resize', this, size.width, size.height);
40062 * Returns this panel's title
40065 getTitle : function(){
40067 if (typeof(this.title) != 'object') {
40072 for (var k in this.title) {
40073 if (!this.title.hasOwnProperty(k)) {
40077 if (k.indexOf('-') >= 0) {
40078 var s = k.split('-');
40079 for (var i = 0; i<s.length; i++) {
40080 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40083 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40090 * Set this panel's title
40091 * @param {String} title
40093 setTitle : function(title){
40094 this.title = title;
40096 this.region.updatePanelTitle(this, title);
40101 * Returns true is this panel was configured to be closable
40102 * @return {Boolean}
40104 isClosable : function(){
40105 return this.closable;
40108 beforeSlide : function(){
40110 this.resizeEl.clip();
40113 afterSlide : function(){
40115 this.resizeEl.unclip();
40119 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40120 * Will fail silently if the {@link #setUrl} method has not been called.
40121 * This does not activate the panel, just updates its content.
40123 refresh : function(){
40124 if(this.refreshDelegate){
40125 this.loaded = false;
40126 this.refreshDelegate();
40131 * Destroys this panel
40133 destroy : function(){
40134 this.el.removeAllListeners();
40135 var tempEl = document.createElement("span");
40136 tempEl.appendChild(this.el.dom);
40137 tempEl.innerHTML = "";
40143 * form - if the content panel contains a form - this is a reference to it.
40144 * @type {Roo.form.Form}
40148 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40149 * This contains a reference to it.
40155 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40165 * @param {Object} cfg Xtype definition of item to add.
40169 getChildContainer: function () {
40170 return this.getEl();
40175 var ret = new Roo.factory(cfg);
40180 if (cfg.xtype.match(/^Form$/)) {
40183 //if (this.footer) {
40184 // el = this.footer.container.insertSibling(false, 'before');
40186 el = this.el.createChild();
40189 this.form = new Roo.form.Form(cfg);
40192 if ( this.form.allItems.length) {
40193 this.form.render(el.dom);
40197 // should only have one of theses..
40198 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40199 // views.. should not be just added - used named prop 'view''
40201 cfg.el = this.el.appendChild(document.createElement("div"));
40204 var ret = new Roo.factory(cfg);
40206 ret.render && ret.render(false, ''); // render blank..
40216 * @class Roo.bootstrap.panel.Grid
40217 * @extends Roo.bootstrap.panel.Content
40219 * Create a new GridPanel.
40220 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40221 * @param {Object} config A the config object
40227 Roo.bootstrap.panel.Grid = function(config)
40231 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40232 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40234 config.el = this.wrapper;
40235 //this.el = this.wrapper;
40237 if (config.container) {
40238 // ctor'ed from a Border/panel.grid
40241 this.wrapper.setStyle("overflow", "hidden");
40242 this.wrapper.addClass('roo-grid-container');
40247 if(config.toolbar){
40248 var tool_el = this.wrapper.createChild();
40249 this.toolbar = Roo.factory(config.toolbar);
40251 if (config.toolbar.items) {
40252 ti = config.toolbar.items ;
40253 delete config.toolbar.items ;
40257 this.toolbar.render(tool_el);
40258 for(var i =0;i < ti.length;i++) {
40259 // Roo.log(['add child', items[i]]);
40260 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40262 this.toolbar.items = nitems;
40264 delete config.toolbar;
40267 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40268 config.grid.scrollBody = true;;
40269 config.grid.monitorWindowResize = false; // turn off autosizing
40270 config.grid.autoHeight = false;
40271 config.grid.autoWidth = false;
40273 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40275 if (config.background) {
40276 // render grid on panel activation (if panel background)
40277 this.on('activate', function(gp) {
40278 if (!gp.grid.rendered) {
40279 gp.grid.render(this.wrapper);
40280 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40285 this.grid.render(this.wrapper);
40286 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40289 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40290 // ??? needed ??? config.el = this.wrapper;
40295 // xtype created footer. - not sure if will work as we normally have to render first..
40296 if (this.footer && !this.footer.el && this.footer.xtype) {
40298 var ctr = this.grid.getView().getFooterPanel(true);
40299 this.footer.dataSource = this.grid.dataSource;
40300 this.footer = Roo.factory(this.footer, Roo);
40301 this.footer.render(ctr);
40311 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40312 getId : function(){
40313 return this.grid.id;
40317 * Returns the grid for this panel
40318 * @return {Roo.bootstrap.Table}
40320 getGrid : function(){
40324 setSize : function(width, height){
40325 if(!this.ignoreResize(width, height)){
40326 var grid = this.grid;
40327 var size = this.adjustForComponents(width, height);
40328 // tfoot is not a footer?
40331 var gridel = grid.getGridEl();
40332 gridel.setSize(size.width, size.height);
40334 var tbd = grid.getGridEl().select('tbody', true).first();
40335 var thd = grid.getGridEl().select('thead',true).first();
40336 var tbf= grid.getGridEl().select('tfoot', true).first();
40339 size.height -= tbf.getHeight();
40342 size.height -= thd.getHeight();
40345 tbd.setSize(size.width, size.height );
40346 // this is for the account management tab -seems to work there.
40347 var thd = grid.getGridEl().select('thead',true).first();
40349 // tbd.setSize(size.width, size.height - thd.getHeight());
40358 beforeSlide : function(){
40359 this.grid.getView().scroller.clip();
40362 afterSlide : function(){
40363 this.grid.getView().scroller.unclip();
40366 destroy : function(){
40367 this.grid.destroy();
40369 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40374 * @class Roo.bootstrap.panel.Nest
40375 * @extends Roo.bootstrap.panel.Content
40377 * Create a new Panel, that can contain a layout.Border.
40380 * @param {Roo.BorderLayout} layout The layout for this panel
40381 * @param {String/Object} config A string to set only the title or a config object
40383 Roo.bootstrap.panel.Nest = function(config)
40385 // construct with only one argument..
40386 /* FIXME - implement nicer consturctors
40387 if (layout.layout) {
40389 layout = config.layout;
40390 delete config.layout;
40392 if (layout.xtype && !layout.getEl) {
40393 // then layout needs constructing..
40394 layout = Roo.factory(layout, Roo);
40398 config.el = config.layout.getEl();
40400 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40402 config.layout.monitorWindowResize = false; // turn off autosizing
40403 this.layout = config.layout;
40404 this.layout.getEl().addClass("roo-layout-nested-layout");
40405 this.layout.parent = this;
40412 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40414 setSize : function(width, height){
40415 if(!this.ignoreResize(width, height)){
40416 var size = this.adjustForComponents(width, height);
40417 var el = this.layout.getEl();
40418 if (size.height < 1) {
40419 el.setWidth(size.width);
40421 el.setSize(size.width, size.height);
40423 var touch = el.dom.offsetWidth;
40424 this.layout.layout();
40425 // ie requires a double layout on the first pass
40426 if(Roo.isIE && !this.initialized){
40427 this.initialized = true;
40428 this.layout.layout();
40433 // activate all subpanels if not currently active..
40435 setActiveState : function(active){
40436 this.active = active;
40437 this.setActiveClass(active);
40440 this.fireEvent("deactivate", this);
40444 this.fireEvent("activate", this);
40445 // not sure if this should happen before or after..
40446 if (!this.layout) {
40447 return; // should not happen..
40450 for (var r in this.layout.regions) {
40451 reg = this.layout.getRegion(r);
40452 if (reg.getActivePanel()) {
40453 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40454 reg.setActivePanel(reg.getActivePanel());
40457 if (!reg.panels.length) {
40460 reg.showPanel(reg.getPanel(0));
40469 * Returns the nested BorderLayout for this panel
40470 * @return {Roo.BorderLayout}
40472 getLayout : function(){
40473 return this.layout;
40477 * Adds a xtype elements to the layout of the nested panel
40481 xtype : 'ContentPanel',
40488 xtype : 'NestedLayoutPanel',
40494 items : [ ... list of content panels or nested layout panels.. ]
40498 * @param {Object} cfg Xtype definition of item to add.
40500 addxtype : function(cfg) {
40501 return this.layout.addxtype(cfg);
40506 * Ext JS Library 1.1.1
40507 * Copyright(c) 2006-2007, Ext JS, LLC.
40509 * Originally Released Under LGPL - original licence link has changed is not relivant.
40512 * <script type="text/javascript">
40515 * @class Roo.TabPanel
40516 * @extends Roo.util.Observable
40517 * A lightweight tab container.
40521 // basic tabs 1, built from existing content
40522 var tabs = new Roo.TabPanel("tabs1");
40523 tabs.addTab("script", "View Script");
40524 tabs.addTab("markup", "View Markup");
40525 tabs.activate("script");
40527 // more advanced tabs, built from javascript
40528 var jtabs = new Roo.TabPanel("jtabs");
40529 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40531 // set up the UpdateManager
40532 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40533 var updater = tab2.getUpdateManager();
40534 updater.setDefaultUrl("ajax1.htm");
40535 tab2.on('activate', updater.refresh, updater, true);
40537 // Use setUrl for Ajax loading
40538 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40539 tab3.setUrl("ajax2.htm", null, true);
40542 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40545 jtabs.activate("jtabs-1");
40548 * Create a new TabPanel.
40549 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40550 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40552 Roo.bootstrap.panel.Tabs = function(config){
40554 * The container element for this TabPanel.
40555 * @type Roo.Element
40557 this.el = Roo.get(config.el);
40560 if(typeof config == "boolean"){
40561 this.tabPosition = config ? "bottom" : "top";
40563 Roo.apply(this, config);
40567 if(this.tabPosition == "bottom"){
40568 // if tabs are at the bottom = create the body first.
40569 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40570 this.el.addClass("roo-tabs-bottom");
40572 // next create the tabs holders
40574 if (this.tabPosition == "west"){
40576 var reg = this.region; // fake it..
40578 if (!reg.mgr.parent) {
40581 reg = reg.mgr.parent.region;
40583 Roo.log("got nest?");
40585 if (reg.mgr.getRegion('west')) {
40586 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40587 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40588 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40589 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40590 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40598 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40599 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40600 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40601 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40606 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40609 // finally - if tabs are at the top, then create the body last..
40610 if(this.tabPosition != "bottom"){
40611 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40612 * @type Roo.Element
40614 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40615 this.el.addClass("roo-tabs-top");
40619 this.bodyEl.setStyle("position", "relative");
40621 this.active = null;
40622 this.activateDelegate = this.activate.createDelegate(this);
40627 * Fires when the active tab changes
40628 * @param {Roo.TabPanel} this
40629 * @param {Roo.TabPanelItem} activePanel The new active tab
40633 * @event beforetabchange
40634 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40635 * @param {Roo.TabPanel} this
40636 * @param {Object} e Set cancel to true on this object to cancel the tab change
40637 * @param {Roo.TabPanelItem} tab The tab being changed to
40639 "beforetabchange" : true
40642 Roo.EventManager.onWindowResize(this.onResize, this);
40643 this.cpad = this.el.getPadding("lr");
40644 this.hiddenCount = 0;
40647 // toolbar on the tabbar support...
40648 if (this.toolbar) {
40649 alert("no toolbar support yet");
40650 this.toolbar = false;
40652 var tcfg = this.toolbar;
40653 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40654 this.toolbar = new Roo.Toolbar(tcfg);
40655 if (Roo.isSafari) {
40656 var tbl = tcfg.container.child('table', true);
40657 tbl.setAttribute('width', '100%');
40665 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40668 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40670 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40672 tabPosition : "top",
40674 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40676 currentTabWidth : 0,
40678 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40682 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40686 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40688 preferredTabWidth : 175,
40690 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40692 resizeTabs : false,
40694 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40696 monitorResize : true,
40698 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40700 toolbar : false, // set by caller..
40702 region : false, /// set by caller
40704 disableTooltips : true, // not used yet...
40707 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40708 * @param {String} id The id of the div to use <b>or create</b>
40709 * @param {String} text The text for the tab
40710 * @param {String} content (optional) Content to put in the TabPanelItem body
40711 * @param {Boolean} closable (optional) True to create a close icon on the tab
40712 * @return {Roo.TabPanelItem} The created TabPanelItem
40714 addTab : function(id, text, content, closable, tpl)
40716 var item = new Roo.bootstrap.panel.TabItem({
40720 closable : closable,
40723 this.addTabItem(item);
40725 item.setContent(content);
40731 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40732 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40733 * @return {Roo.TabPanelItem}
40735 getTab : function(id){
40736 return this.items[id];
40740 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40741 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40743 hideTab : function(id){
40744 var t = this.items[id];
40747 this.hiddenCount++;
40748 this.autoSizeTabs();
40753 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40754 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40756 unhideTab : function(id){
40757 var t = this.items[id];
40759 t.setHidden(false);
40760 this.hiddenCount--;
40761 this.autoSizeTabs();
40766 * Adds an existing {@link Roo.TabPanelItem}.
40767 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40769 addTabItem : function(item)
40771 this.items[item.id] = item;
40772 this.items.push(item);
40773 this.autoSizeTabs();
40774 // if(this.resizeTabs){
40775 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40776 // this.autoSizeTabs();
40778 // item.autoSize();
40783 * Removes a {@link Roo.TabPanelItem}.
40784 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40786 removeTab : function(id){
40787 var items = this.items;
40788 var tab = items[id];
40789 if(!tab) { return; }
40790 var index = items.indexOf(tab);
40791 if(this.active == tab && items.length > 1){
40792 var newTab = this.getNextAvailable(index);
40797 this.stripEl.dom.removeChild(tab.pnode.dom);
40798 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40799 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40801 items.splice(index, 1);
40802 delete this.items[tab.id];
40803 tab.fireEvent("close", tab);
40804 tab.purgeListeners();
40805 this.autoSizeTabs();
40808 getNextAvailable : function(start){
40809 var items = this.items;
40811 // look for a next tab that will slide over to
40812 // replace the one being removed
40813 while(index < items.length){
40814 var item = items[++index];
40815 if(item && !item.isHidden()){
40819 // if one isn't found select the previous tab (on the left)
40822 var item = items[--index];
40823 if(item && !item.isHidden()){
40831 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40832 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40834 disableTab : function(id){
40835 var tab = this.items[id];
40836 if(tab && this.active != tab){
40842 * Enables a {@link Roo.TabPanelItem} that is disabled.
40843 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40845 enableTab : function(id){
40846 var tab = this.items[id];
40851 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40852 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40853 * @return {Roo.TabPanelItem} The TabPanelItem.
40855 activate : function(id)
40857 //Roo.log('activite:' + id);
40859 var tab = this.items[id];
40863 if(tab == this.active || tab.disabled){
40867 this.fireEvent("beforetabchange", this, e, tab);
40868 if(e.cancel !== true && !tab.disabled){
40870 this.active.hide();
40872 this.active = this.items[id];
40873 this.active.show();
40874 this.fireEvent("tabchange", this, this.active);
40880 * Gets the active {@link Roo.TabPanelItem}.
40881 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40883 getActiveTab : function(){
40884 return this.active;
40888 * Updates the tab body element to fit the height of the container element
40889 * for overflow scrolling
40890 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40892 syncHeight : function(targetHeight){
40893 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40894 var bm = this.bodyEl.getMargins();
40895 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40896 this.bodyEl.setHeight(newHeight);
40900 onResize : function(){
40901 if(this.monitorResize){
40902 this.autoSizeTabs();
40907 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40909 beginUpdate : function(){
40910 this.updating = true;
40914 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40916 endUpdate : function(){
40917 this.updating = false;
40918 this.autoSizeTabs();
40922 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40924 autoSizeTabs : function()
40926 var count = this.items.length;
40927 var vcount = count - this.hiddenCount;
40930 this.stripEl.hide();
40932 this.stripEl.show();
40935 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40940 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40941 var availWidth = Math.floor(w / vcount);
40942 var b = this.stripBody;
40943 if(b.getWidth() > w){
40944 var tabs = this.items;
40945 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40946 if(availWidth < this.minTabWidth){
40947 /*if(!this.sleft){ // incomplete scrolling code
40948 this.createScrollButtons();
40951 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40954 if(this.currentTabWidth < this.preferredTabWidth){
40955 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40961 * Returns the number of tabs in this TabPanel.
40964 getCount : function(){
40965 return this.items.length;
40969 * Resizes all the tabs to the passed width
40970 * @param {Number} The new width
40972 setTabWidth : function(width){
40973 this.currentTabWidth = width;
40974 for(var i = 0, len = this.items.length; i < len; i++) {
40975 if(!this.items[i].isHidden()) {
40976 this.items[i].setWidth(width);
40982 * Destroys this TabPanel
40983 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40985 destroy : function(removeEl){
40986 Roo.EventManager.removeResizeListener(this.onResize, this);
40987 for(var i = 0, len = this.items.length; i < len; i++){
40988 this.items[i].purgeListeners();
40990 if(removeEl === true){
40991 this.el.update("");
40996 createStrip : function(container)
40998 var strip = document.createElement("nav");
40999 strip.className = Roo.bootstrap.version == 4 ?
41000 "navbar-light bg-light" :
41001 "navbar navbar-default"; //"x-tabs-wrap";
41002 container.appendChild(strip);
41006 createStripList : function(strip)
41008 // div wrapper for retard IE
41009 // returns the "tr" element.
41010 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41011 //'<div class="x-tabs-strip-wrap">'+
41012 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41013 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41014 return strip.firstChild; //.firstChild.firstChild.firstChild;
41016 createBody : function(container)
41018 var body = document.createElement("div");
41019 Roo.id(body, "tab-body");
41020 //Roo.fly(body).addClass("x-tabs-body");
41021 Roo.fly(body).addClass("tab-content");
41022 container.appendChild(body);
41025 createItemBody :function(bodyEl, id){
41026 var body = Roo.getDom(id);
41028 body = document.createElement("div");
41031 //Roo.fly(body).addClass("x-tabs-item-body");
41032 Roo.fly(body).addClass("tab-pane");
41033 bodyEl.insertBefore(body, bodyEl.firstChild);
41037 createStripElements : function(stripEl, text, closable, tpl)
41039 var td = document.createElement("li"); // was td..
41040 td.className = 'nav-item';
41042 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41045 stripEl.appendChild(td);
41047 td.className = "x-tabs-closable";
41048 if(!this.closeTpl){
41049 this.closeTpl = new Roo.Template(
41050 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41051 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41052 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41055 var el = this.closeTpl.overwrite(td, {"text": text});
41056 var close = el.getElementsByTagName("div")[0];
41057 var inner = el.getElementsByTagName("em")[0];
41058 return {"el": el, "close": close, "inner": inner};
41061 // not sure what this is..
41062 // if(!this.tabTpl){
41063 //this.tabTpl = new Roo.Template(
41064 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41065 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41067 // this.tabTpl = new Roo.Template(
41068 // '<a href="#">' +
41069 // '<span unselectable="on"' +
41070 // (this.disableTooltips ? '' : ' title="{text}"') +
41071 // ' >{text}</span></a>'
41077 var template = tpl || this.tabTpl || false;
41080 template = new Roo.Template(
41081 Roo.bootstrap.version == 4 ?
41083 '<a class="nav-link" href="#" unselectable="on"' +
41084 (this.disableTooltips ? '' : ' title="{text}"') +
41087 '<a class="nav-link" href="#">' +
41088 '<span unselectable="on"' +
41089 (this.disableTooltips ? '' : ' title="{text}"') +
41090 ' >{text}</span></a>'
41095 switch (typeof(template)) {
41099 template = new Roo.Template(template);
41105 var el = template.overwrite(td, {"text": text});
41107 var inner = el.getElementsByTagName("span")[0];
41109 return {"el": el, "inner": inner};
41117 * @class Roo.TabPanelItem
41118 * @extends Roo.util.Observable
41119 * Represents an individual item (tab plus body) in a TabPanel.
41120 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41121 * @param {String} id The id of this TabPanelItem
41122 * @param {String} text The text for the tab of this TabPanelItem
41123 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41125 Roo.bootstrap.panel.TabItem = function(config){
41127 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41128 * @type Roo.TabPanel
41130 this.tabPanel = config.panel;
41132 * The id for this TabPanelItem
41135 this.id = config.id;
41137 this.disabled = false;
41139 this.text = config.text;
41141 this.loaded = false;
41142 this.closable = config.closable;
41145 * The body element for this TabPanelItem.
41146 * @type Roo.Element
41148 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41149 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41150 this.bodyEl.setStyle("display", "block");
41151 this.bodyEl.setStyle("zoom", "1");
41152 //this.hideAction();
41154 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41156 this.el = Roo.get(els.el);
41157 this.inner = Roo.get(els.inner, true);
41158 this.textEl = Roo.bootstrap.version == 4 ?
41159 this.el : Roo.get(this.el.dom.firstChild, true);
41161 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41162 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41165 // this.el.on("mousedown", this.onTabMouseDown, this);
41166 this.el.on("click", this.onTabClick, this);
41168 if(config.closable){
41169 var c = Roo.get(els.close, true);
41170 c.dom.title = this.closeText;
41171 c.addClassOnOver("close-over");
41172 c.on("click", this.closeClick, this);
41178 * Fires when this tab becomes the active tab.
41179 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41180 * @param {Roo.TabPanelItem} this
41184 * @event beforeclose
41185 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41186 * @param {Roo.TabPanelItem} this
41187 * @param {Object} e Set cancel to true on this object to cancel the close.
41189 "beforeclose": true,
41192 * Fires when this tab is closed.
41193 * @param {Roo.TabPanelItem} this
41197 * @event deactivate
41198 * Fires when this tab is no longer the active tab.
41199 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41200 * @param {Roo.TabPanelItem} this
41202 "deactivate" : true
41204 this.hidden = false;
41206 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41209 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41211 purgeListeners : function(){
41212 Roo.util.Observable.prototype.purgeListeners.call(this);
41213 this.el.removeAllListeners();
41216 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41219 this.status_node.addClass("active");
41222 this.tabPanel.stripWrap.repaint();
41224 this.fireEvent("activate", this.tabPanel, this);
41228 * Returns true if this tab is the active tab.
41229 * @return {Boolean}
41231 isActive : function(){
41232 return this.tabPanel.getActiveTab() == this;
41236 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41239 this.status_node.removeClass("active");
41241 this.fireEvent("deactivate", this.tabPanel, this);
41244 hideAction : function(){
41245 this.bodyEl.hide();
41246 this.bodyEl.setStyle("position", "absolute");
41247 this.bodyEl.setLeft("-20000px");
41248 this.bodyEl.setTop("-20000px");
41251 showAction : function(){
41252 this.bodyEl.setStyle("position", "relative");
41253 this.bodyEl.setTop("");
41254 this.bodyEl.setLeft("");
41255 this.bodyEl.show();
41259 * Set the tooltip for the tab.
41260 * @param {String} tooltip The tab's tooltip
41262 setTooltip : function(text){
41263 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41264 this.textEl.dom.qtip = text;
41265 this.textEl.dom.removeAttribute('title');
41267 this.textEl.dom.title = text;
41271 onTabClick : function(e){
41272 e.preventDefault();
41273 this.tabPanel.activate(this.id);
41276 onTabMouseDown : function(e){
41277 e.preventDefault();
41278 this.tabPanel.activate(this.id);
41281 getWidth : function(){
41282 return this.inner.getWidth();
41285 setWidth : function(width){
41286 var iwidth = width - this.linode.getPadding("lr");
41287 this.inner.setWidth(iwidth);
41288 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41289 this.linode.setWidth(width);
41293 * Show or hide the tab
41294 * @param {Boolean} hidden True to hide or false to show.
41296 setHidden : function(hidden){
41297 this.hidden = hidden;
41298 this.linode.setStyle("display", hidden ? "none" : "");
41302 * Returns true if this tab is "hidden"
41303 * @return {Boolean}
41305 isHidden : function(){
41306 return this.hidden;
41310 * Returns the text for this tab
41313 getText : function(){
41317 autoSize : function(){
41318 //this.el.beginMeasure();
41319 this.textEl.setWidth(1);
41321 * #2804 [new] Tabs in Roojs
41322 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41324 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41325 //this.el.endMeasure();
41329 * Sets the text for the tab (Note: this also sets the tooltip text)
41330 * @param {String} text The tab's text and tooltip
41332 setText : function(text){
41334 this.textEl.update(text);
41335 this.setTooltip(text);
41336 //if(!this.tabPanel.resizeTabs){
41337 // this.autoSize();
41341 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41343 activate : function(){
41344 this.tabPanel.activate(this.id);
41348 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41350 disable : function(){
41351 if(this.tabPanel.active != this){
41352 this.disabled = true;
41353 this.status_node.addClass("disabled");
41358 * Enables this TabPanelItem if it was previously disabled.
41360 enable : function(){
41361 this.disabled = false;
41362 this.status_node.removeClass("disabled");
41366 * Sets the content for this TabPanelItem.
41367 * @param {String} content The content
41368 * @param {Boolean} loadScripts true to look for and load scripts
41370 setContent : function(content, loadScripts){
41371 this.bodyEl.update(content, loadScripts);
41375 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41376 * @return {Roo.UpdateManager} The UpdateManager
41378 getUpdateManager : function(){
41379 return this.bodyEl.getUpdateManager();
41383 * Set a URL to be used to load the content for this TabPanelItem.
41384 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41385 * @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)
41386 * @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)
41387 * @return {Roo.UpdateManager} The UpdateManager
41389 setUrl : function(url, params, loadOnce){
41390 if(this.refreshDelegate){
41391 this.un('activate', this.refreshDelegate);
41393 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41394 this.on("activate", this.refreshDelegate);
41395 return this.bodyEl.getUpdateManager();
41399 _handleRefresh : function(url, params, loadOnce){
41400 if(!loadOnce || !this.loaded){
41401 var updater = this.bodyEl.getUpdateManager();
41402 updater.update(url, params, this._setLoaded.createDelegate(this));
41407 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41408 * Will fail silently if the setUrl method has not been called.
41409 * This does not activate the panel, just updates its content.
41411 refresh : function(){
41412 if(this.refreshDelegate){
41413 this.loaded = false;
41414 this.refreshDelegate();
41419 _setLoaded : function(){
41420 this.loaded = true;
41424 closeClick : function(e){
41427 this.fireEvent("beforeclose", this, o);
41428 if(o.cancel !== true){
41429 this.tabPanel.removeTab(this.id);
41433 * The text displayed in the tooltip for the close icon.
41436 closeText : "Close this tab"
41439 * This script refer to:
41440 * Title: International Telephone Input
41441 * Author: Jack O'Connor
41442 * Code version: v12.1.12
41443 * Availability: https://github.com/jackocnr/intl-tel-input.git
41446 Roo.bootstrap.PhoneInputData = function() {
41449 "Afghanistan (افغانستان)",
41454 "Albania (Shqipëri)",
41459 "Algeria (الجزائر)",
41484 "Antigua and Barbuda",
41494 "Armenia (Հայաստան)",
41510 "Austria (Österreich)",
41515 "Azerbaijan (Azərbaycan)",
41525 "Bahrain (البحرين)",
41530 "Bangladesh (বাংলাদেশ)",
41540 "Belarus (Беларусь)",
41545 "Belgium (België)",
41575 "Bosnia and Herzegovina (Босна и Херцеговина)",
41590 "British Indian Ocean Territory",
41595 "British Virgin Islands",
41605 "Bulgaria (България)",
41615 "Burundi (Uburundi)",
41620 "Cambodia (កម្ពុជា)",
41625 "Cameroon (Cameroun)",
41634 ["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"]
41637 "Cape Verde (Kabu Verdi)",
41642 "Caribbean Netherlands",
41653 "Central African Republic (République centrafricaine)",
41673 "Christmas Island",
41679 "Cocos (Keeling) Islands",
41690 "Comoros (جزر القمر)",
41695 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41700 "Congo (Republic) (Congo-Brazzaville)",
41720 "Croatia (Hrvatska)",
41741 "Czech Republic (Česká republika)",
41746 "Denmark (Danmark)",
41761 "Dominican Republic (República Dominicana)",
41765 ["809", "829", "849"]
41783 "Equatorial Guinea (Guinea Ecuatorial)",
41803 "Falkland Islands (Islas Malvinas)",
41808 "Faroe Islands (Føroyar)",
41829 "French Guiana (Guyane française)",
41834 "French Polynesia (Polynésie française)",
41849 "Georgia (საქართველო)",
41854 "Germany (Deutschland)",
41874 "Greenland (Kalaallit Nunaat)",
41911 "Guinea-Bissau (Guiné Bissau)",
41936 "Hungary (Magyarország)",
41941 "Iceland (Ísland)",
41961 "Iraq (العراق)",
41977 "Israel (ישראל)",
42004 "Jordan (الأردن)",
42009 "Kazakhstan (Казахстан)",
42030 "Kuwait (الكويت)",
42035 "Kyrgyzstan (Кыргызстан)",
42045 "Latvia (Latvija)",
42050 "Lebanon (لبنان)",
42065 "Libya (ليبيا)",
42075 "Lithuania (Lietuva)",
42090 "Macedonia (FYROM) (Македонија)",
42095 "Madagascar (Madagasikara)",
42125 "Marshall Islands",
42135 "Mauritania (موريتانيا)",
42140 "Mauritius (Moris)",
42161 "Moldova (Republica Moldova)",
42171 "Mongolia (Монгол)",
42176 "Montenegro (Crna Gora)",
42186 "Morocco (المغرب)",
42192 "Mozambique (Moçambique)",
42197 "Myanmar (Burma) (မြန်မာ)",
42202 "Namibia (Namibië)",
42217 "Netherlands (Nederland)",
42222 "New Caledonia (Nouvelle-Calédonie)",
42257 "North Korea (조선 민주주의 인민 공화국)",
42262 "Northern Mariana Islands",
42278 "Pakistan (پاکستان)",
42288 "Palestine (فلسطين)",
42298 "Papua New Guinea",
42340 "Réunion (La Réunion)",
42346 "Romania (România)",
42362 "Saint Barthélemy",
42373 "Saint Kitts and Nevis",
42383 "Saint Martin (Saint-Martin (partie française))",
42389 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42394 "Saint Vincent and the Grenadines",
42409 "São Tomé and Príncipe (São Tomé e Príncipe)",
42414 "Saudi Arabia (المملكة العربية السعودية)",
42419 "Senegal (Sénégal)",
42449 "Slovakia (Slovensko)",
42454 "Slovenia (Slovenija)",
42464 "Somalia (Soomaaliya)",
42474 "South Korea (대한민국)",
42479 "South Sudan (جنوب السودان)",
42489 "Sri Lanka (ශ්රී ලංකාව)",
42494 "Sudan (السودان)",
42504 "Svalbard and Jan Mayen",
42515 "Sweden (Sverige)",
42520 "Switzerland (Schweiz)",
42525 "Syria (سوريا)",
42570 "Trinidad and Tobago",
42575 "Tunisia (تونس)",
42580 "Turkey (Türkiye)",
42590 "Turks and Caicos Islands",
42600 "U.S. Virgin Islands",
42610 "Ukraine (Україна)",
42615 "United Arab Emirates (الإمارات العربية المتحدة)",
42637 "Uzbekistan (Oʻzbekiston)",
42647 "Vatican City (Città del Vaticano)",
42658 "Vietnam (Việt Nam)",
42663 "Wallis and Futuna (Wallis-et-Futuna)",
42668 "Western Sahara (الصحراء الغربية)",
42674 "Yemen (اليمن)",
42698 * This script refer to:
42699 * Title: International Telephone Input
42700 * Author: Jack O'Connor
42701 * Code version: v12.1.12
42702 * Availability: https://github.com/jackocnr/intl-tel-input.git
42706 * @class Roo.bootstrap.PhoneInput
42707 * @extends Roo.bootstrap.TriggerField
42708 * An input with International dial-code selection
42710 * @cfg {String} defaultDialCode default '+852'
42711 * @cfg {Array} preferedCountries default []
42714 * Create a new PhoneInput.
42715 * @param {Object} config Configuration options
42718 Roo.bootstrap.PhoneInput = function(config) {
42719 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42722 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42724 listWidth: undefined,
42726 selectedClass: 'active',
42728 invalidClass : "has-warning",
42730 validClass: 'has-success',
42732 allowed: '0123456789',
42737 * @cfg {String} defaultDialCode The default dial code when initializing the input
42739 defaultDialCode: '+852',
42742 * @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
42744 preferedCountries: false,
42746 getAutoCreate : function()
42748 var data = Roo.bootstrap.PhoneInputData();
42749 var align = this.labelAlign || this.parentLabelAlign();
42752 this.allCountries = [];
42753 this.dialCodeMapping = [];
42755 for (var i = 0; i < data.length; i++) {
42757 this.allCountries[i] = {
42761 priority: c[3] || 0,
42762 areaCodes: c[4] || null
42764 this.dialCodeMapping[c[2]] = {
42767 priority: c[3] || 0,
42768 areaCodes: c[4] || null
42780 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42781 maxlength: this.max_length,
42782 cls : 'form-control tel-input',
42783 autocomplete: 'new-password'
42786 var hiddenInput = {
42789 cls: 'hidden-tel-input'
42793 hiddenInput.name = this.name;
42796 if (this.disabled) {
42797 input.disabled = true;
42800 var flag_container = {
42817 cls: this.hasFeedback ? 'has-feedback' : '',
42823 cls: 'dial-code-holder',
42830 cls: 'roo-select2-container input-group',
42837 if (this.fieldLabel.length) {
42840 tooltip: 'This field is required'
42846 cls: 'control-label',
42852 html: this.fieldLabel
42855 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42861 if(this.indicatorpos == 'right') {
42862 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42869 if(align == 'left') {
42877 if(this.labelWidth > 12){
42878 label.style = "width: " + this.labelWidth + 'px';
42880 if(this.labelWidth < 13 && this.labelmd == 0){
42881 this.labelmd = this.labelWidth;
42883 if(this.labellg > 0){
42884 label.cls += ' col-lg-' + this.labellg;
42885 input.cls += ' col-lg-' + (12 - this.labellg);
42887 if(this.labelmd > 0){
42888 label.cls += ' col-md-' + this.labelmd;
42889 container.cls += ' col-md-' + (12 - this.labelmd);
42891 if(this.labelsm > 0){
42892 label.cls += ' col-sm-' + this.labelsm;
42893 container.cls += ' col-sm-' + (12 - this.labelsm);
42895 if(this.labelxs > 0){
42896 label.cls += ' col-xs-' + this.labelxs;
42897 container.cls += ' col-xs-' + (12 - this.labelxs);
42907 var settings = this;
42909 ['xs','sm','md','lg'].map(function(size){
42910 if (settings[size]) {
42911 cfg.cls += ' col-' + size + '-' + settings[size];
42915 this.store = new Roo.data.Store({
42916 proxy : new Roo.data.MemoryProxy({}),
42917 reader : new Roo.data.JsonReader({
42928 'name' : 'dialCode',
42932 'name' : 'priority',
42936 'name' : 'areaCodes',
42943 if(!this.preferedCountries) {
42944 this.preferedCountries = [
42951 var p = this.preferedCountries.reverse();
42954 for (var i = 0; i < p.length; i++) {
42955 for (var j = 0; j < this.allCountries.length; j++) {
42956 if(this.allCountries[j].iso2 == p[i]) {
42957 var t = this.allCountries[j];
42958 this.allCountries.splice(j,1);
42959 this.allCountries.unshift(t);
42965 this.store.proxy.data = {
42967 data: this.allCountries
42973 initEvents : function()
42976 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42978 this.indicator = this.indicatorEl();
42979 this.flag = this.flagEl();
42980 this.dialCodeHolder = this.dialCodeHolderEl();
42982 this.trigger = this.el.select('div.flag-box',true).first();
42983 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42988 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42989 _this.list.setWidth(lw);
42992 this.list.on('mouseover', this.onViewOver, this);
42993 this.list.on('mousemove', this.onViewMove, this);
42994 this.inputEl().on("keyup", this.onKeyUp, this);
42995 this.inputEl().on("keypress", this.onKeyPress, this);
42997 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42999 this.view = new Roo.View(this.list, this.tpl, {
43000 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43003 this.view.on('click', this.onViewClick, this);
43004 this.setValue(this.defaultDialCode);
43007 onTriggerClick : function(e)
43009 Roo.log('trigger click');
43014 if(this.isExpanded()){
43016 this.hasFocus = false;
43018 this.store.load({});
43019 this.hasFocus = true;
43024 isExpanded : function()
43026 return this.list.isVisible();
43029 collapse : function()
43031 if(!this.isExpanded()){
43035 Roo.get(document).un('mousedown', this.collapseIf, this);
43036 Roo.get(document).un('mousewheel', this.collapseIf, this);
43037 this.fireEvent('collapse', this);
43041 expand : function()
43045 if(this.isExpanded() || !this.hasFocus){
43049 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43050 this.list.setWidth(lw);
43053 this.restrictHeight();
43055 Roo.get(document).on('mousedown', this.collapseIf, this);
43056 Roo.get(document).on('mousewheel', this.collapseIf, this);
43058 this.fireEvent('expand', this);
43061 restrictHeight : function()
43063 this.list.alignTo(this.inputEl(), this.listAlign);
43064 this.list.alignTo(this.inputEl(), this.listAlign);
43067 onViewOver : function(e, t)
43069 if(this.inKeyMode){
43072 var item = this.view.findItemFromChild(t);
43075 var index = this.view.indexOf(item);
43076 this.select(index, false);
43081 onViewClick : function(view, doFocus, el, e)
43083 var index = this.view.getSelectedIndexes()[0];
43085 var r = this.store.getAt(index);
43088 this.onSelect(r, index);
43090 if(doFocus !== false && !this.blockFocus){
43091 this.inputEl().focus();
43095 onViewMove : function(e, t)
43097 this.inKeyMode = false;
43100 select : function(index, scrollIntoView)
43102 this.selectedIndex = index;
43103 this.view.select(index);
43104 if(scrollIntoView !== false){
43105 var el = this.view.getNode(index);
43107 this.list.scrollChildIntoView(el, false);
43112 createList : function()
43114 this.list = Roo.get(document.body).createChild({
43116 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43117 style: 'display:none'
43120 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43123 collapseIf : function(e)
43125 var in_combo = e.within(this.el);
43126 var in_list = e.within(this.list);
43127 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43129 if (in_combo || in_list || is_list) {
43135 onSelect : function(record, index)
43137 if(this.fireEvent('beforeselect', this, record, index) !== false){
43139 this.setFlagClass(record.data.iso2);
43140 this.setDialCode(record.data.dialCode);
43141 this.hasFocus = false;
43143 this.fireEvent('select', this, record, index);
43147 flagEl : function()
43149 var flag = this.el.select('div.flag',true).first();
43156 dialCodeHolderEl : function()
43158 var d = this.el.select('input.dial-code-holder',true).first();
43165 setDialCode : function(v)
43167 this.dialCodeHolder.dom.value = '+'+v;
43170 setFlagClass : function(n)
43172 this.flag.dom.className = 'flag '+n;
43175 getValue : function()
43177 var v = this.inputEl().getValue();
43178 if(this.dialCodeHolder) {
43179 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43184 setValue : function(v)
43186 var d = this.getDialCode(v);
43188 //invalid dial code
43189 if(v.length == 0 || !d || d.length == 0) {
43191 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43192 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43198 this.setFlagClass(this.dialCodeMapping[d].iso2);
43199 this.setDialCode(d);
43200 this.inputEl().dom.value = v.replace('+'+d,'');
43201 this.hiddenEl().dom.value = this.getValue();
43206 getDialCode : function(v)
43210 if (v.length == 0) {
43211 return this.dialCodeHolder.dom.value;
43215 if (v.charAt(0) != "+") {
43218 var numericChars = "";
43219 for (var i = 1; i < v.length; i++) {
43220 var c = v.charAt(i);
43223 if (this.dialCodeMapping[numericChars]) {
43224 dialCode = v.substr(1, i);
43226 if (numericChars.length == 4) {
43236 this.setValue(this.defaultDialCode);
43240 hiddenEl : function()
43242 return this.el.select('input.hidden-tel-input',true).first();
43245 // after setting val
43246 onKeyUp : function(e){
43247 this.setValue(this.getValue());
43250 onKeyPress : function(e){
43251 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43258 * @class Roo.bootstrap.MoneyField
43259 * @extends Roo.bootstrap.ComboBox
43260 * Bootstrap MoneyField class
43263 * Create a new MoneyField.
43264 * @param {Object} config Configuration options
43267 Roo.bootstrap.MoneyField = function(config) {
43269 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43273 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43276 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43278 allowDecimals : true,
43280 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43282 decimalSeparator : ".",
43284 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43286 decimalPrecision : 0,
43288 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43290 allowNegative : true,
43292 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43296 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43298 minValue : Number.NEGATIVE_INFINITY,
43300 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43302 maxValue : Number.MAX_VALUE,
43304 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43306 minText : "The minimum value for this field is {0}",
43308 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43310 maxText : "The maximum value for this field is {0}",
43312 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43313 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43315 nanText : "{0} is not a valid number",
43317 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43321 * @cfg {String} defaults currency of the MoneyField
43322 * value should be in lkey
43324 defaultCurrency : false,
43326 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43328 thousandsDelimiter : false,
43330 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43341 getAutoCreate : function()
43343 var align = this.labelAlign || this.parentLabelAlign();
43355 cls : 'form-control roo-money-amount-input',
43356 autocomplete: 'new-password'
43359 var hiddenInput = {
43363 cls: 'hidden-number-input'
43366 if(this.max_length) {
43367 input.maxlength = this.max_length;
43371 hiddenInput.name = this.name;
43374 if (this.disabled) {
43375 input.disabled = true;
43378 var clg = 12 - this.inputlg;
43379 var cmd = 12 - this.inputmd;
43380 var csm = 12 - this.inputsm;
43381 var cxs = 12 - this.inputxs;
43385 cls : 'row roo-money-field',
43389 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43393 cls: 'roo-select2-container input-group',
43397 cls : 'form-control roo-money-currency-input',
43398 autocomplete: 'new-password',
43400 name : this.currencyName
43404 cls : 'input-group-addon',
43418 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43422 cls: this.hasFeedback ? 'has-feedback' : '',
43433 if (this.fieldLabel.length) {
43436 tooltip: 'This field is required'
43442 cls: 'control-label',
43448 html: this.fieldLabel
43451 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43457 if(this.indicatorpos == 'right') {
43458 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43465 if(align == 'left') {
43473 if(this.labelWidth > 12){
43474 label.style = "width: " + this.labelWidth + 'px';
43476 if(this.labelWidth < 13 && this.labelmd == 0){
43477 this.labelmd = this.labelWidth;
43479 if(this.labellg > 0){
43480 label.cls += ' col-lg-' + this.labellg;
43481 input.cls += ' col-lg-' + (12 - this.labellg);
43483 if(this.labelmd > 0){
43484 label.cls += ' col-md-' + this.labelmd;
43485 container.cls += ' col-md-' + (12 - this.labelmd);
43487 if(this.labelsm > 0){
43488 label.cls += ' col-sm-' + this.labelsm;
43489 container.cls += ' col-sm-' + (12 - this.labelsm);
43491 if(this.labelxs > 0){
43492 label.cls += ' col-xs-' + this.labelxs;
43493 container.cls += ' col-xs-' + (12 - this.labelxs);
43504 var settings = this;
43506 ['xs','sm','md','lg'].map(function(size){
43507 if (settings[size]) {
43508 cfg.cls += ' col-' + size + '-' + settings[size];
43515 initEvents : function()
43517 this.indicator = this.indicatorEl();
43519 this.initCurrencyEvent();
43521 this.initNumberEvent();
43524 initCurrencyEvent : function()
43527 throw "can not find store for combo";
43530 this.store = Roo.factory(this.store, Roo.data);
43531 this.store.parent = this;
43535 this.triggerEl = this.el.select('.input-group-addon', true).first();
43537 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43542 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43543 _this.list.setWidth(lw);
43546 this.list.on('mouseover', this.onViewOver, this);
43547 this.list.on('mousemove', this.onViewMove, this);
43548 this.list.on('scroll', this.onViewScroll, this);
43551 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43554 this.view = new Roo.View(this.list, this.tpl, {
43555 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43558 this.view.on('click', this.onViewClick, this);
43560 this.store.on('beforeload', this.onBeforeLoad, this);
43561 this.store.on('load', this.onLoad, this);
43562 this.store.on('loadexception', this.onLoadException, this);
43564 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43565 "up" : function(e){
43566 this.inKeyMode = true;
43570 "down" : function(e){
43571 if(!this.isExpanded()){
43572 this.onTriggerClick();
43574 this.inKeyMode = true;
43579 "enter" : function(e){
43582 if(this.fireEvent("specialkey", this, e)){
43583 this.onViewClick(false);
43589 "esc" : function(e){
43593 "tab" : function(e){
43596 if(this.fireEvent("specialkey", this, e)){
43597 this.onViewClick(false);
43605 doRelay : function(foo, bar, hname){
43606 if(hname == 'down' || this.scope.isExpanded()){
43607 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43615 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43619 initNumberEvent : function(e)
43621 this.inputEl().on("keydown" , this.fireKey, this);
43622 this.inputEl().on("focus", this.onFocus, this);
43623 this.inputEl().on("blur", this.onBlur, this);
43625 this.inputEl().relayEvent('keyup', this);
43627 if(this.indicator){
43628 this.indicator.addClass('invisible');
43631 this.originalValue = this.getValue();
43633 if(this.validationEvent == 'keyup'){
43634 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43635 this.inputEl().on('keyup', this.filterValidation, this);
43637 else if(this.validationEvent !== false){
43638 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43641 if(this.selectOnFocus){
43642 this.on("focus", this.preFocus, this);
43645 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43646 this.inputEl().on("keypress", this.filterKeys, this);
43648 this.inputEl().relayEvent('keypress', this);
43651 var allowed = "0123456789";
43653 if(this.allowDecimals){
43654 allowed += this.decimalSeparator;
43657 if(this.allowNegative){
43661 if(this.thousandsDelimiter) {
43665 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43667 var keyPress = function(e){
43669 var k = e.getKey();
43671 var c = e.getCharCode();
43674 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43675 allowed.indexOf(String.fromCharCode(c)) === -1
43681 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43685 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43690 this.inputEl().on("keypress", keyPress, this);
43694 onTriggerClick : function(e)
43701 this.loadNext = false;
43703 if(this.isExpanded()){
43708 this.hasFocus = true;
43710 if(this.triggerAction == 'all') {
43711 this.doQuery(this.allQuery, true);
43715 this.doQuery(this.getRawValue());
43718 getCurrency : function()
43720 var v = this.currencyEl().getValue();
43725 restrictHeight : function()
43727 this.list.alignTo(this.currencyEl(), this.listAlign);
43728 this.list.alignTo(this.currencyEl(), this.listAlign);
43731 onViewClick : function(view, doFocus, el, e)
43733 var index = this.view.getSelectedIndexes()[0];
43735 var r = this.store.getAt(index);
43738 this.onSelect(r, index);
43742 onSelect : function(record, index){
43744 if(this.fireEvent('beforeselect', this, record, index) !== false){
43746 this.setFromCurrencyData(index > -1 ? record.data : false);
43750 this.fireEvent('select', this, record, index);
43754 setFromCurrencyData : function(o)
43758 this.lastCurrency = o;
43760 if (this.currencyField) {
43761 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43763 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43766 this.lastSelectionText = currency;
43768 //setting default currency
43769 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43770 this.setCurrency(this.defaultCurrency);
43774 this.setCurrency(currency);
43777 setFromData : function(o)
43781 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43783 this.setFromCurrencyData(c);
43788 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43790 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43793 this.setValue(value);
43797 setCurrency : function(v)
43799 this.currencyValue = v;
43802 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43807 setValue : function(v)
43809 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43815 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43817 this.inputEl().dom.value = (v == '') ? '' :
43818 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43820 if(!this.allowZero && v === '0') {
43821 this.hiddenEl().dom.value = '';
43822 this.inputEl().dom.value = '';
43829 getRawValue : function()
43831 var v = this.inputEl().getValue();
43836 getValue : function()
43838 return this.fixPrecision(this.parseValue(this.getRawValue()));
43841 parseValue : function(value)
43843 if(this.thousandsDelimiter) {
43845 r = new RegExp(",", "g");
43846 value = value.replace(r, "");
43849 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43850 return isNaN(value) ? '' : value;
43854 fixPrecision : function(value)
43856 if(this.thousandsDelimiter) {
43858 r = new RegExp(",", "g");
43859 value = value.replace(r, "");
43862 var nan = isNaN(value);
43864 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43865 return nan ? '' : value;
43867 return parseFloat(value).toFixed(this.decimalPrecision);
43870 decimalPrecisionFcn : function(v)
43872 return Math.floor(v);
43875 validateValue : function(value)
43877 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43881 var num = this.parseValue(value);
43884 this.markInvalid(String.format(this.nanText, value));
43888 if(num < this.minValue){
43889 this.markInvalid(String.format(this.minText, this.minValue));
43893 if(num > this.maxValue){
43894 this.markInvalid(String.format(this.maxText, this.maxValue));
43901 validate : function()
43903 if(this.disabled || this.allowBlank){
43908 var currency = this.getCurrency();
43910 if(this.validateValue(this.getRawValue()) && currency.length){
43915 this.markInvalid();
43919 getName: function()
43924 beforeBlur : function()
43930 var v = this.parseValue(this.getRawValue());
43937 onBlur : function()
43941 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43942 //this.el.removeClass(this.focusClass);
43945 this.hasFocus = false;
43947 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43951 var v = this.getValue();
43953 if(String(v) !== String(this.startValue)){
43954 this.fireEvent('change', this, v, this.startValue);
43957 this.fireEvent("blur", this);
43960 inputEl : function()
43962 return this.el.select('.roo-money-amount-input', true).first();
43965 currencyEl : function()
43967 return this.el.select('.roo-money-currency-input', true).first();
43970 hiddenEl : function()
43972 return this.el.select('input.hidden-number-input',true).first();
43976 * @class Roo.bootstrap.BezierSignature
43977 * @extends Roo.bootstrap.Component
43978 * Bootstrap BezierSignature class
43979 * This script refer to:
43980 * Title: Signature Pad
43982 * Availability: https://github.com/szimek/signature_pad
43985 * Create a new BezierSignature
43986 * @param {Object} config The config object
43989 Roo.bootstrap.BezierSignature = function(config){
43990 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43996 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44003 mouse_btn_down: true,
44006 * @cfg {int} canvas height
44008 canvas_height: '200px',
44011 * @cfg {float|function} Radius of a single dot.
44016 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44021 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44026 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44031 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44036 * @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.
44038 bg_color: 'rgba(0, 0, 0, 0)',
44041 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44043 dot_color: 'black',
44046 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44048 velocity_filter_weight: 0.7,
44051 * @cfg {function} Callback when stroke begin.
44056 * @cfg {function} Callback when stroke end.
44060 getAutoCreate : function()
44062 var cls = 'roo-signature column';
44065 cls += ' ' + this.cls;
44075 for(var i = 0; i < col_sizes.length; i++) {
44076 if(this[col_sizes[i]]) {
44077 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44087 cls: 'roo-signature-body',
44091 cls: 'roo-signature-body-canvas',
44092 height: this.canvas_height,
44093 width: this.canvas_width
44100 style: 'display: none'
44108 initEvents: function()
44110 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44112 var canvas = this.canvasEl();
44114 // mouse && touch event swapping...
44115 canvas.dom.style.touchAction = 'none';
44116 canvas.dom.style.msTouchAction = 'none';
44118 this.mouse_btn_down = false;
44119 canvas.on('mousedown', this._handleMouseDown, this);
44120 canvas.on('mousemove', this._handleMouseMove, this);
44121 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44123 if (window.PointerEvent) {
44124 canvas.on('pointerdown', this._handleMouseDown, this);
44125 canvas.on('pointermove', this._handleMouseMove, this);
44126 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44129 if ('ontouchstart' in window) {
44130 canvas.on('touchstart', this._handleTouchStart, this);
44131 canvas.on('touchmove', this._handleTouchMove, this);
44132 canvas.on('touchend', this._handleTouchEnd, this);
44135 Roo.EventManager.onWindowResize(this.resize, this, true);
44137 // file input event
44138 this.fileEl().on('change', this.uploadImage, this);
44145 resize: function(){
44147 var canvas = this.canvasEl().dom;
44148 var ctx = this.canvasElCtx();
44149 var img_data = false;
44151 if(canvas.width > 0) {
44152 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44154 // setting canvas width will clean img data
44157 var style = window.getComputedStyle ?
44158 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44160 var padding_left = parseInt(style.paddingLeft) || 0;
44161 var padding_right = parseInt(style.paddingRight) || 0;
44163 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44166 ctx.putImageData(img_data, 0, 0);
44170 _handleMouseDown: function(e)
44172 if (e.browserEvent.which === 1) {
44173 this.mouse_btn_down = true;
44174 this.strokeBegin(e);
44178 _handleMouseMove: function (e)
44180 if (this.mouse_btn_down) {
44181 this.strokeMoveUpdate(e);
44185 _handleMouseUp: function (e)
44187 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44188 this.mouse_btn_down = false;
44193 _handleTouchStart: function (e) {
44195 e.preventDefault();
44196 if (e.browserEvent.targetTouches.length === 1) {
44197 // var touch = e.browserEvent.changedTouches[0];
44198 // this.strokeBegin(touch);
44200 this.strokeBegin(e); // assume e catching the correct xy...
44204 _handleTouchMove: function (e) {
44205 e.preventDefault();
44206 // var touch = event.targetTouches[0];
44207 // _this._strokeMoveUpdate(touch);
44208 this.strokeMoveUpdate(e);
44211 _handleTouchEnd: function (e) {
44212 var wasCanvasTouched = e.target === this.canvasEl().dom;
44213 if (wasCanvasTouched) {
44214 e.preventDefault();
44215 // var touch = event.changedTouches[0];
44216 // _this._strokeEnd(touch);
44221 reset: function () {
44222 this._lastPoints = [];
44223 this._lastVelocity = 0;
44224 this._lastWidth = (this.min_width + this.max_width) / 2;
44225 this.canvasElCtx().fillStyle = this.dot_color;
44228 strokeMoveUpdate: function(e)
44230 this.strokeUpdate(e);
44232 if (this.throttle) {
44233 this.throttleStroke(this.strokeUpdate, this.throttle);
44236 this.strokeUpdate(e);
44240 strokeBegin: function(e)
44242 var newPointGroup = {
44243 color: this.dot_color,
44247 if (typeof this.onBegin === 'function') {
44251 this.curve_data.push(newPointGroup);
44253 this.strokeUpdate(e);
44256 strokeUpdate: function(e)
44258 var rect = this.canvasEl().dom.getBoundingClientRect();
44259 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44260 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44261 var lastPoints = lastPointGroup.points;
44262 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44263 var isLastPointTooClose = lastPoint
44264 ? point.distanceTo(lastPoint) <= this.min_distance
44266 var color = lastPointGroup.color;
44267 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44268 var curve = this.addPoint(point);
44270 this.drawDot({color: color, point: point});
44273 this.drawCurve({color: color, curve: curve});
44283 strokeEnd: function(e)
44285 this.strokeUpdate(e);
44286 if (typeof this.onEnd === 'function') {
44291 addPoint: function (point) {
44292 var _lastPoints = this._lastPoints;
44293 _lastPoints.push(point);
44294 if (_lastPoints.length > 2) {
44295 if (_lastPoints.length === 3) {
44296 _lastPoints.unshift(_lastPoints[0]);
44298 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44299 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44300 _lastPoints.shift();
44306 calculateCurveWidths: function (startPoint, endPoint) {
44307 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44308 (1 - this.velocity_filter_weight) * this._lastVelocity;
44310 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44313 start: this._lastWidth
44316 this._lastVelocity = velocity;
44317 this._lastWidth = newWidth;
44321 drawDot: function (_a) {
44322 var color = _a.color, point = _a.point;
44323 var ctx = this.canvasElCtx();
44324 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44326 this.drawCurveSegment(point.x, point.y, width);
44328 ctx.fillStyle = color;
44332 drawCurve: function (_a) {
44333 var color = _a.color, curve = _a.curve;
44334 var ctx = this.canvasElCtx();
44335 var widthDelta = curve.endWidth - curve.startWidth;
44336 var drawSteps = Math.floor(curve.length()) * 2;
44338 ctx.fillStyle = color;
44339 for (var i = 0; i < drawSteps; i += 1) {
44340 var t = i / drawSteps;
44346 var x = uuu * curve.startPoint.x;
44347 x += 3 * uu * t * curve.control1.x;
44348 x += 3 * u * tt * curve.control2.x;
44349 x += ttt * curve.endPoint.x;
44350 var y = uuu * curve.startPoint.y;
44351 y += 3 * uu * t * curve.control1.y;
44352 y += 3 * u * tt * curve.control2.y;
44353 y += ttt * curve.endPoint.y;
44354 var width = curve.startWidth + ttt * widthDelta;
44355 this.drawCurveSegment(x, y, width);
44361 drawCurveSegment: function (x, y, width) {
44362 var ctx = this.canvasElCtx();
44364 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44365 this.is_empty = false;
44370 var ctx = this.canvasElCtx();
44371 var canvas = this.canvasEl().dom;
44372 ctx.fillStyle = this.bg_color;
44373 ctx.clearRect(0, 0, canvas.width, canvas.height);
44374 ctx.fillRect(0, 0, canvas.width, canvas.height);
44375 this.curve_data = [];
44377 this.is_empty = true;
44382 return this.el.select('input',true).first();
44385 canvasEl: function()
44387 return this.el.select('canvas',true).first();
44390 canvasElCtx: function()
44392 return this.el.select('canvas',true).first().dom.getContext('2d');
44395 getImage: function(type)
44397 if(this.is_empty) {
44402 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44405 drawFromImage: function(img_src)
44407 var img = new Image();
44409 img.onload = function(){
44410 this.canvasElCtx().drawImage(img, 0, 0);
44415 this.is_empty = false;
44418 selectImage: function()
44420 this.fileEl().dom.click();
44423 uploadImage: function(e)
44425 var reader = new FileReader();
44427 reader.onload = function(e){
44428 var img = new Image();
44429 img.onload = function(){
44431 this.canvasElCtx().drawImage(img, 0, 0);
44433 img.src = e.target.result;
44436 reader.readAsDataURL(e.target.files[0]);
44439 // Bezier Point Constructor
44440 Point: (function () {
44441 function Point(x, y, time) {
44444 this.time = time || Date.now();
44446 Point.prototype.distanceTo = function (start) {
44447 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44449 Point.prototype.equals = function (other) {
44450 return this.x === other.x && this.y === other.y && this.time === other.time;
44452 Point.prototype.velocityFrom = function (start) {
44453 return this.time !== start.time
44454 ? this.distanceTo(start) / (this.time - start.time)
44461 // Bezier Constructor
44462 Bezier: (function () {
44463 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44464 this.startPoint = startPoint;
44465 this.control2 = control2;
44466 this.control1 = control1;
44467 this.endPoint = endPoint;
44468 this.startWidth = startWidth;
44469 this.endWidth = endWidth;
44471 Bezier.fromPoints = function (points, widths, scope) {
44472 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44473 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44474 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44476 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44477 var dx1 = s1.x - s2.x;
44478 var dy1 = s1.y - s2.y;
44479 var dx2 = s2.x - s3.x;
44480 var dy2 = s2.y - s3.y;
44481 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44482 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44483 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44484 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44485 var dxm = m1.x - m2.x;
44486 var dym = m1.y - m2.y;
44487 var k = l2 / (l1 + l2);
44488 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44489 var tx = s2.x - cm.x;
44490 var ty = s2.y - cm.y;
44492 c1: new scope.Point(m1.x + tx, m1.y + ty),
44493 c2: new scope.Point(m2.x + tx, m2.y + ty)
44496 Bezier.prototype.length = function () {
44501 for (var i = 0; i <= steps; i += 1) {
44503 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44504 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44506 var xdiff = cx - px;
44507 var ydiff = cy - py;
44508 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44515 Bezier.prototype.point = function (t, start, c1, c2, end) {
44516 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44517 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44518 + (3.0 * c2 * (1.0 - t) * t * t)
44519 + (end * t * t * t);
44524 throttleStroke: function(fn, wait) {
44525 if (wait === void 0) { wait = 250; }
44527 var timeout = null;
44531 var later = function () {
44532 previous = Date.now();
44534 result = fn.apply(storedContext, storedArgs);
44536 storedContext = null;
44540 return function wrapper() {
44542 for (var _i = 0; _i < arguments.length; _i++) {
44543 args[_i] = arguments[_i];
44545 var now = Date.now();
44546 var remaining = wait - (now - previous);
44547 storedContext = this;
44549 if (remaining <= 0 || remaining > wait) {
44551 clearTimeout(timeout);
44555 result = fn.apply(storedContext, storedArgs);
44557 storedContext = null;
44561 else if (!timeout) {
44562 timeout = window.setTimeout(later, remaining);