2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2079 layoutCls : function()
2083 Roo.log(this.margin_bottom.length);
2084 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2090 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2095 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2101 // more generic support?
2109 // Roo.log("Call onRender: " + this.xtype);
2110 /* We are looking at something like this.
2112 <img src="..." class="card-img-top" alt="...">
2113 <div class="card-body">
2114 <h5 class="card-title">Card title</h5>
2115 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117 >> this bit is really the body...
2118 <div> << we will ad dthis in hopefully it will not break shit.
2120 ** card text does not actually have any styling...
2122 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2125 <a href="#" class="card-link">Card link</a>
2128 <div class="card-footer">
2129 <small class="text-muted">Last updated 3 mins ago</small>
2133 getAutoCreate : function(){
2141 if (this.weight.length && this.weight != 'light') {
2142 cfg.cls += ' text-white';
2144 cfg.cls += ' text-dark'; // need as it's nested..
2146 if (this.weight.length) {
2147 cfg.cls += ' bg-' + this.weight;
2150 cfg.cls += ' ' + this.layoutCls();
2153 var hdr_ctr = false;
2154 if (this.header.length) {
2156 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2165 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2171 if (this.collapsable) {
2174 cls : 'd-block user-select-none',
2178 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2183 hdr.cn.push(hdr_ctr);
2188 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2193 if (this.header_image.length) {
2196 cls : 'card-img-top',
2197 src: this.header_image // escape?
2202 cls : 'card-img-top d-none'
2208 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2212 if (this.collapsable || this.rotateable) {
2215 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2222 if (this.title.length) {
2226 src: this.title // escape?
2230 if (this.subtitle.length) {
2234 src: this.subtitle // escape?
2240 cls : 'roo-card-body-ctr'
2243 if (this.html.length) {
2249 // fixme ? handle objects?
2251 if (this.footer.length) {
2254 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2259 cfg.cn.push({cls : 'card-footer d-none'});
2268 getCardHeader : function()
2270 var ret = this.el.select('.card-header',true).first();
2271 if (ret.hasClass('d-none')) {
2272 ret.removeClass('d-none');
2277 getCardFooter : function()
2279 var ret = this.el.select('.card-footer',true).first();
2280 if (ret.hasClass('d-none')) {
2281 ret.removeClass('d-none');
2286 getCardImageTop : function()
2288 var ret = this.el.select('.card-img-top',true).first();
2289 if (ret.hasClass('d-none')) {
2290 ret.removeClass('d-none');
2296 getChildContainer : function()
2302 return this.el.select('.roo-card-body-ctr',true).first();
2305 initEvents: function()
2307 this.bodyEl = this.el.select('.card-body',true).first();
2308 this.containerEl = this.getChildContainer();
2310 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311 containerScroll: true,
2312 ddGroup: this.drag_group || 'default_card_drag_group'
2314 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316 if (this.dropable) {
2317 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318 containerScroll: true,
2319 ddGroup: this.drop_group || 'default_card_drag_group'
2321 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2328 if (this.collapsable) {
2329 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331 if (this.rotateable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334 this.collapsableEl = this.el.select('.roo-collapsable').first();
2336 this.footerEl = this.el.select('.card-footer').first();
2337 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339 this.headerEl = this.el.select('.card-header',true).first();
2342 this.el.addClass('roo-card-rotated');
2343 this.fireEvent('rotate', this, true);
2347 getDragData : function(e)
2349 var target = this.getEl();
2351 //this.handleSelection(e);
2356 nodes: this.getEl(),
2361 dragData.ddel = target.dom ; // the div element
2362 Roo.log(target.getWidth( ));
2363 dragData.ddel.style.width = target.getWidth() + 'px';
2370 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2371 * whole Element becomes the target, and this causes the drop gesture to append.
2373 getTargetFromEvent : function(e, dragged_card_el)
2375 var target = e.getTarget();
2376 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377 target = target.parentNode;
2388 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389 // see if target is one of the 'cards'...
2392 //Roo.log(this.items.length);
2395 var last_card_n = 0;
2397 for (var i = 0;i< this.items.length;i++) {
2399 if (!this.items[i].el.hasClass('card')) {
2402 pos = this.getDropPoint(e, this.items[i].el.dom);
2404 cards_len = ret.cards.length;
2405 //Roo.log(this.items[i].el.dom.id);
2406 ret.cards.push(this.items[i]);
2408 if (ret.card_n < 0 && pos == 'above') {
2409 ret.position = cards_len > 0 ? 'below' : pos;
2410 ret.items_n = i > 0 ? i - 1 : 0;
2411 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2412 ret.card = ret.cards[ret.card_n];
2415 if (!ret.cards.length) {
2417 ret.position = 'below';
2421 // could not find a card.. stick it at the end..
2422 if (ret.card_n < 0) {
2423 ret.card_n = last_card_n;
2424 ret.card = ret.cards[last_card_n];
2425 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426 ret.position = 'below';
2429 if (this.items[ret.items_n].el == dragged_card_el) {
2433 if (ret.position == 'below') {
2434 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2436 if (card_after && card_after.el == dragged_card_el) {
2443 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2445 if (card_before && card_before.el == dragged_card_el) {
2452 onNodeEnter : function(n, dd, e, data){
2455 onNodeOver : function(n, dd, e, data)
2458 var target_info = this.getTargetFromEvent(e,data.source.el);
2459 if (target_info === false) {
2460 this.dropPlaceHolder('hide');
2463 Roo.log(['getTargetFromEvent', target_info ]);
2466 this.dropPlaceHolder('show', target_info,data);
2470 onNodeOut : function(n, dd, e, data){
2471 this.dropPlaceHolder('hide');
2474 onNodeDrop : function(n, dd, e, data)
2477 // call drop - return false if
2479 // this could actually fail - if the Network drops..
2480 // we will ignore this at present..- client should probably reload
2481 // the whole set of cards if stuff like that fails.
2484 var info = this.getTargetFromEvent(e,data.source.el);
2485 if (info === false) {
2488 this.dropPlaceHolder('hide');
2494 this.acceptCard(data.source, info.position, info.card, info.items_n);
2498 firstChildCard : function()
2500 for (var i = 0;i< this.items.length;i++) {
2502 if (!this.items[i].el.hasClass('card')) {
2505 return this.items[i];
2507 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2512 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2514 acceptCard : function(move_card, position, next_to_card )
2516 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2522 move_card.parent().removeCard(move_card);
2525 var dom = move_card.el.dom;
2526 dom.style.width = ''; // clear with - which is set by drag.
2528 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529 var cardel = next_to_card.el.dom;
2531 if (position == 'above' ) {
2532 cardel.parentNode.insertBefore(dom, cardel);
2533 } else if (cardel.nextSibling) {
2534 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2536 cardel.parentNode.append(dom);
2539 // card container???
2540 this.containerEl.dom.append(dom);
2543 //FIXME HANDLE card = true
2545 // add this to the correct place in items.
2547 // remove Card from items.
2550 if (this.items.length) {
2552 //Roo.log([info.items_n, info.position, this.items.length]);
2553 for (var i =0; i < this.items.length; i++) {
2554 if (i == to_items_n && position == 'above') {
2555 nitems.push(move_card);
2557 nitems.push(this.items[i]);
2558 if (i == to_items_n && position == 'below') {
2559 nitems.push(move_card);
2562 this.items = nitems;
2563 Roo.log(this.items);
2565 this.items.push(move_card);
2568 move_card.parentId = this.id;
2574 removeCard : function(c)
2576 this.items = this.items.filter(function(e) { return e != c });
2579 dom.parentNode.removeChild(dom);
2580 dom.style.width = ''; // clear with - which is set by drag.
2585 /** Decide whether to drop above or below a View node. */
2586 getDropPoint : function(e, n, dd)
2591 if (n == this.containerEl.dom) {
2594 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595 var c = t + (b - t) / 2;
2596 var y = Roo.lib.Event.getPageY(e);
2603 onToggleCollapse : function(e)
2605 if (this.collapsed) {
2606 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607 this.collapsableEl.addClass('show');
2608 this.collapsed = false;
2611 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612 this.collapsableEl.removeClass('show');
2613 this.collapsed = true;
2618 onToggleRotate : function(e)
2620 this.collapsableEl.removeClass('show');
2621 this.footerEl.removeClass('d-none');
2622 this.el.removeClass('roo-card-rotated');
2623 this.el.removeClass('d-none');
2626 this.collapsableEl.addClass('show');
2627 this.rotated = false;
2628 this.fireEvent('rotate', this, this.rotated);
2631 this.el.addClass('roo-card-rotated');
2632 this.footerEl.addClass('d-none');
2633 this.el.select('.roo-collapsable').removeClass('show');
2635 this.rotated = true;
2636 this.fireEvent('rotate', this, this.rotated);
2640 dropPlaceHolder: function (action, info, data)
2642 if (this.dropEl === false) {
2643 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2647 this.dropEl.removeClass(['d-none', 'd-block']);
2648 if (action == 'hide') {
2650 this.dropEl.addClass('d-none');
2653 // FIXME - info.card == true!!!
2654 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2656 if (info.card !== true) {
2657 var cardel = info.card.el.dom;
2659 if (info.position == 'above') {
2660 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661 } else if (cardel.nextSibling) {
2662 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2664 cardel.parentNode.append(this.dropEl.dom);
2667 // card container???
2668 this.containerEl.dom.append(this.dropEl.dom);
2671 this.dropEl.addClass('d-block roo-card-dropzone');
2673 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2680 setHeaderText: function(html)
2682 this.headerContainerEl.dom.innerHTML = html;
2691 * Card header - holder for the card header elements.
2696 * @class Roo.bootstrap.CardHeader
2697 * @extends Roo.bootstrap.Element
2698 * Bootstrap CardHeader class
2700 * Create a new Card Header - that you can embed children into
2701 * @param {Object} config The config object
2704 Roo.bootstrap.CardHeader = function(config){
2705 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2711 container_method : 'getCardHeader'
2724 * Card footer - holder for the card footer elements.
2729 * @class Roo.bootstrap.CardFooter
2730 * @extends Roo.bootstrap.Element
2731 * Bootstrap CardFooter class
2733 * Create a new Card Footer - that you can embed children into
2734 * @param {Object} config The config object
2737 Roo.bootstrap.CardFooter = function(config){
2738 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2744 container_method : 'getCardFooter'
2757 * Card header - holder for the card header elements.
2762 * @class Roo.bootstrap.CardImageTop
2763 * @extends Roo.bootstrap.Element
2764 * Bootstrap CardImageTop class
2766 * Create a new Card Image Top container
2767 * @param {Object} config The config object
2770 Roo.bootstrap.CardImageTop = function(config){
2771 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2777 container_method : 'getCardImageTop'
2795 * @class Roo.bootstrap.Img
2796 * @extends Roo.bootstrap.Component
2797 * Bootstrap Img class
2798 * @cfg {Boolean} imgResponsive false | true
2799 * @cfg {String} border rounded | circle | thumbnail
2800 * @cfg {String} src image source
2801 * @cfg {String} alt image alternative text
2802 * @cfg {String} href a tag href
2803 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804 * @cfg {String} xsUrl xs image source
2805 * @cfg {String} smUrl sm image source
2806 * @cfg {String} mdUrl md image source
2807 * @cfg {String} lgUrl lg image source
2810 * Create a new Input
2811 * @param {Object} config The config object
2814 Roo.bootstrap.Img = function(config){
2815 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2821 * The img click event for the img.
2822 * @param {Roo.EventObject} e
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2830 imgResponsive: true,
2840 getAutoCreate : function()
2842 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843 return this.createSingleImg();
2848 cls: 'roo-image-responsive-group',
2853 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2855 if(!_this[size + 'Url']){
2861 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862 html: _this.html || cfg.html,
2863 src: _this[size + 'Url']
2866 img.cls += ' roo-image-responsive-' + size;
2868 var s = ['xs', 'sm', 'md', 'lg'];
2870 s.splice(s.indexOf(size), 1);
2872 Roo.each(s, function(ss){
2873 img.cls += ' hidden-' + ss;
2876 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877 cfg.cls += ' img-' + _this.border;
2881 cfg.alt = _this.alt;
2894 a.target = _this.target;
2898 cfg.cn.push((_this.href) ? a : img);
2905 createSingleImg : function()
2909 cls: (this.imgResponsive) ? 'img-responsive' : '',
2911 src : 'about:blank' // just incase src get's set to undefined?!?
2914 cfg.html = this.html || cfg.html;
2916 cfg.src = this.src || cfg.src;
2918 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919 cfg.cls += ' img-' + this.border;
2936 a.target = this.target;
2941 return (this.href) ? a : cfg;
2944 initEvents: function()
2947 this.el.on('click', this.onClick, this);
2952 onClick : function(e)
2954 Roo.log('img onclick');
2955 this.fireEvent('click', this, e);
2958 * Sets the url of the image - used to update it
2959 * @param {String} url the url of the image
2962 setSrc : function(url)
2966 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967 this.el.dom.src = url;
2971 this.el.select('img', true).first().dom.src = url;
2987 * @class Roo.bootstrap.Link
2988 * @extends Roo.bootstrap.Component
2989 * Bootstrap Link Class
2990 * @cfg {String} alt image alternative text
2991 * @cfg {String} href a tag href
2992 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993 * @cfg {String} html the content of the link.
2994 * @cfg {String} anchor name for the anchor link
2995 * @cfg {String} fa - favicon
2997 * @cfg {Boolean} preventDefault (true | false) default false
3001 * Create a new Input
3002 * @param {Object} config The config object
3005 Roo.bootstrap.Link = function(config){
3006 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3012 * The img click event for the img.
3013 * @param {Roo.EventObject} e
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3023 preventDefault: false,
3029 getAutoCreate : function()
3031 var html = this.html || '';
3033 if (this.fa !== false) {
3034 html = '<i class="fa fa-' + this.fa + '"></i>';
3039 // anchor's do not require html/href...
3040 if (this.anchor === false) {
3042 cfg.href = this.href || '#';
3044 cfg.name = this.anchor;
3045 if (this.html !== false || this.fa !== false) {
3048 if (this.href !== false) {
3049 cfg.href = this.href;
3053 if(this.alt !== false){
3058 if(this.target !== false) {
3059 cfg.target = this.target;
3065 initEvents: function() {
3067 if(!this.href || this.preventDefault){
3068 this.el.on('click', this.onClick, this);
3072 onClick : function(e)
3074 if(this.preventDefault){
3077 //Roo.log('img onclick');
3078 this.fireEvent('click', this, e);
3091 * @class Roo.bootstrap.Header
3092 * @extends Roo.bootstrap.Component
3093 * Bootstrap Header class
3094 * @cfg {String} html content of header
3095 * @cfg {Number} level (1|2|3|4|5|6) default 1
3098 * Create a new Header
3099 * @param {Object} config The config object
3103 Roo.bootstrap.Header = function(config){
3104 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3115 getAutoCreate : function(){
3120 tag: 'h' + (1 *this.level),
3121 html: this.html || ''
3133 * Ext JS Library 1.1.1
3134 * Copyright(c) 2006-2007, Ext JS, LLC.
3136 * Originally Released Under LGPL - original licence link has changed is not relivant.
3139 * <script type="text/javascript">
3143 * @class Roo.bootstrap.MenuMgr
3144 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3147 Roo.bootstrap.MenuMgr = function(){
3148 var menus, active, groups = {}, attached = false, lastShow = new Date();
3150 // private - called when first menu is created
3153 active = new Roo.util.MixedCollection();
3154 Roo.get(document).addKeyListener(27, function(){
3155 if(active.length > 0){
3163 if(active && active.length > 0){
3164 var c = active.clone();
3174 if(active.length < 1){
3175 Roo.get(document).un("mouseup", onMouseDown);
3183 var last = active.last();
3184 lastShow = new Date();
3187 Roo.get(document).on("mouseup", onMouseDown);
3192 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193 m.parentMenu.activeChild = m;
3194 }else if(last && last.isVisible()){
3195 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3200 function onBeforeHide(m){
3202 m.activeChild.hide();
3204 if(m.autoHideTimer){
3205 clearTimeout(m.autoHideTimer);
3206 delete m.autoHideTimer;
3211 function onBeforeShow(m){
3212 var pm = m.parentMenu;
3213 if(!pm && !m.allowOtherMenus){
3215 }else if(pm && pm.activeChild && active != m){
3216 pm.activeChild.hide();
3220 // private this should really trigger on mouseup..
3221 function onMouseDown(e){
3222 Roo.log("on Mouse Up");
3224 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225 Roo.log("MenuManager hideAll");
3234 function onBeforeCheck(mi, state){
3236 var g = groups[mi.group];
3237 for(var i = 0, l = g.length; i < l; i++){
3239 g[i].setChecked(false);
3248 * Hides all menus that are currently visible
3250 hideAll : function(){
3255 register : function(menu){
3259 menus[menu.id] = menu;
3260 menu.on("beforehide", onBeforeHide);
3261 menu.on("hide", onHide);
3262 menu.on("beforeshow", onBeforeShow);
3263 menu.on("show", onShow);
3265 if(g && menu.events["checkchange"]){
3269 groups[g].push(menu);
3270 menu.on("checkchange", onCheck);
3275 * Returns a {@link Roo.menu.Menu} object
3276 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277 * be used to generate and return a new Menu instance.
3279 get : function(menu){
3280 if(typeof menu == "string"){ // menu id
3282 }else if(menu.events){ // menu instance
3285 /*else if(typeof menu.length == 'number'){ // array of menu items?
3286 return new Roo.bootstrap.Menu({items:menu});
3287 }else{ // otherwise, must be a config
3288 return new Roo.bootstrap.Menu(menu);
3295 unregister : function(menu){
3296 delete menus[menu.id];
3297 menu.un("beforehide", onBeforeHide);
3298 menu.un("hide", onHide);
3299 menu.un("beforeshow", onBeforeShow);
3300 menu.un("show", onShow);
3302 if(g && menu.events["checkchange"]){
3303 groups[g].remove(menu);
3304 menu.un("checkchange", onCheck);
3309 registerCheckable : function(menuItem){
3310 var g = menuItem.group;
3315 groups[g].push(menuItem);
3316 menuItem.on("beforecheckchange", onBeforeCheck);
3321 unregisterCheckable : function(menuItem){
3322 var g = menuItem.group;
3324 groups[g].remove(menuItem);
3325 menuItem.un("beforecheckchange", onBeforeCheck);
3337 * @class Roo.bootstrap.Menu
3338 * @extends Roo.bootstrap.Component
3339 * Bootstrap Menu class - container for MenuItems
3340 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341 * @cfg {bool} hidden if the menu should be hidden when rendered.
3342 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3343 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3347 * @param {Object} config The config object
3351 Roo.bootstrap.Menu = function(config){
3352 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353 if (this.registerMenu && this.type != 'treeview') {
3354 Roo.bootstrap.MenuMgr.register(this);
3361 * Fires before this menu is displayed (return false to block)
3362 * @param {Roo.menu.Menu} this
3367 * Fires before this menu is hidden (return false to block)
3368 * @param {Roo.menu.Menu} this
3373 * Fires after this menu is displayed
3374 * @param {Roo.menu.Menu} this
3379 * Fires after this menu is hidden
3380 * @param {Roo.menu.Menu} this
3385 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386 * @param {Roo.menu.Menu} this
3387 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388 * @param {Roo.EventObject} e
3393 * Fires when the mouse is hovering over this menu
3394 * @param {Roo.menu.Menu} this
3395 * @param {Roo.EventObject} e
3396 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3401 * Fires when the mouse exits this menu
3402 * @param {Roo.menu.Menu} this
3403 * @param {Roo.EventObject} e
3404 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3409 * Fires when a menu item contained in this menu is clicked
3410 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411 * @param {Roo.EventObject} e
3415 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3422 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3425 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3427 registerMenu : true,
3429 menuItems :false, // stores the menu items..
3439 getChildContainer : function() {
3443 getAutoCreate : function(){
3445 //if (['right'].indexOf(this.align)!==-1) {
3446 // cfg.cn[1].cls += ' pull-right'
3452 cls : 'dropdown-menu' ,
3453 style : 'z-index:1000'
3457 if (this.type === 'submenu') {
3458 cfg.cls = 'submenu active';
3460 if (this.type === 'treeview') {
3461 cfg.cls = 'treeview-menu';
3466 initEvents : function() {
3468 // Roo.log("ADD event");
3469 // Roo.log(this.triggerEl.dom);
3471 this.triggerEl.on('click', this.onTriggerClick, this);
3473 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3476 if (this.triggerEl.hasClass('nav-item')) {
3477 // dropdown toggle on the 'a' in BS4?
3478 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3480 this.triggerEl.addClass('dropdown-toggle');
3483 this.el.on('touchstart' , this.onTouch, this);
3485 this.el.on('click' , this.onClick, this);
3487 this.el.on("mouseover", this.onMouseOver, this);
3488 this.el.on("mouseout", this.onMouseOut, this);
3492 findTargetItem : function(e)
3494 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3498 //Roo.log(t); Roo.log(t.id);
3500 //Roo.log(this.menuitems);
3501 return this.menuitems.get(t.id);
3503 //return this.items.get(t.menuItemId);
3509 onTouch : function(e)
3511 Roo.log("menu.onTouch");
3512 //e.stopEvent(); this make the user popdown broken
3516 onClick : function(e)
3518 Roo.log("menu.onClick");
3520 var t = this.findTargetItem(e);
3521 if(!t || t.isContainer){
3526 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3527 if(t == this.activeItem && t.shouldDeactivate(e)){
3528 this.activeItem.deactivate();
3529 delete this.activeItem;
3533 this.setActiveItem(t, true);
3541 Roo.log('pass click event');
3545 this.fireEvent("click", this, t, e);
3549 if(!t.href.length || t.href == '#'){
3550 (function() { _this.hide(); }).defer(100);
3555 onMouseOver : function(e){
3556 var t = this.findTargetItem(e);
3559 // if(t.canActivate && !t.disabled){
3560 // this.setActiveItem(t, true);
3564 this.fireEvent("mouseover", this, e, t);
3566 isVisible : function(){
3567 return !this.hidden;
3569 onMouseOut : function(e){
3570 var t = this.findTargetItem(e);
3573 // if(t == this.activeItem && t.shouldDeactivate(e)){
3574 // this.activeItem.deactivate();
3575 // delete this.activeItem;
3578 this.fireEvent("mouseout", this, e, t);
3583 * Displays this menu relative to another element
3584 * @param {String/HTMLElement/Roo.Element} element The element to align to
3585 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586 * the element (defaults to this.defaultAlign)
3587 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3589 show : function(el, pos, parentMenu)
3591 if (false === this.fireEvent("beforeshow", this)) {
3592 Roo.log("show canceled");
3595 this.parentMenu = parentMenu;
3600 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3603 * Displays this menu at a specific xy position
3604 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3607 showAt : function(xy, parentMenu, /* private: */_e){
3608 this.parentMenu = parentMenu;
3613 this.fireEvent("beforeshow", this);
3614 //xy = this.el.adjustForConstraints(xy);
3618 this.hideMenuItems();
3619 this.hidden = false;
3620 this.triggerEl.addClass('open');
3621 this.el.addClass('show');
3623 // reassign x when hitting right
3624 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3628 // reassign y when hitting bottom
3629 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3633 // but the list may align on trigger left or trigger top... should it be a properity?
3635 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3640 this.fireEvent("show", this);
3646 this.doFocus.defer(50, this);
3650 doFocus : function(){
3652 this.focusEl.focus();
3657 * Hides this menu and optionally all parent menus
3658 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3660 hide : function(deep)
3662 if (false === this.fireEvent("beforehide", this)) {
3663 Roo.log("hide canceled");
3666 this.hideMenuItems();
3667 if(this.el && this.isVisible()){
3669 if(this.activeItem){
3670 this.activeItem.deactivate();
3671 this.activeItem = null;
3673 this.triggerEl.removeClass('open');;
3674 this.el.removeClass('show');
3676 this.fireEvent("hide", this);
3678 if(deep === true && this.parentMenu){
3679 this.parentMenu.hide(true);
3683 onTriggerClick : function(e)
3685 Roo.log('trigger click');
3687 var target = e.getTarget();
3689 Roo.log(target.nodeName.toLowerCase());
3691 if(target.nodeName.toLowerCase() === 'i'){
3697 onTriggerPress : function(e)
3699 Roo.log('trigger press');
3700 //Roo.log(e.getTarget());
3701 // Roo.log(this.triggerEl.dom);
3703 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704 var pel = Roo.get(e.getTarget());
3705 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706 Roo.log('is treeview or dropdown?');
3710 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3714 if (this.isVisible()) {
3719 this.show(this.triggerEl, '?', false);
3722 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3729 hideMenuItems : function()
3731 Roo.log("hide Menu Items");
3736 this.el.select('.open',true).each(function(aa) {
3738 aa.removeClass('open');
3742 addxtypeChild : function (tree, cntr) {
3743 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3745 this.menuitems.add(comp);
3757 this.getEl().dom.innerHTML = '';
3758 this.menuitems.clear();
3772 * @class Roo.bootstrap.MenuItem
3773 * @extends Roo.bootstrap.Component
3774 * Bootstrap MenuItem class
3775 * @cfg {String} html the menu label
3776 * @cfg {String} href the link
3777 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779 * @cfg {Boolean} active used on sidebars to highlight active itesm
3780 * @cfg {String} fa favicon to show on left of menu item.
3781 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3785 * Create a new MenuItem
3786 * @param {Object} config The config object
3790 Roo.bootstrap.MenuItem = function(config){
3791 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3796 * The raw click event for the entire grid.
3797 * @param {Roo.bootstrap.MenuItem} this
3798 * @param {Roo.EventObject} e
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3808 preventDefault: false,
3809 isContainer : false,
3813 getAutoCreate : function(){
3815 if(this.isContainer){
3818 cls: 'dropdown-menu-item '
3828 cls : 'dropdown-item',
3833 if (this.fa !== false) {
3836 cls : 'fa fa-' + this.fa
3845 cls: 'dropdown-menu-item',
3848 if (this.parent().type == 'treeview') {
3849 cfg.cls = 'treeview-menu';
3852 cfg.cls += ' active';
3857 anc.href = this.href || cfg.cn[0].href ;
3858 ctag.html = this.html || cfg.cn[0].html ;
3862 initEvents: function()
3864 if (this.parent().type == 'treeview') {
3865 this.el.select('a').on('click', this.onClick, this);
3869 this.menu.parentType = this.xtype;
3870 this.menu.triggerEl = this.el;
3871 this.menu = this.addxtype(Roo.apply({}, this.menu));
3875 onClick : function(e)
3877 Roo.log('item on click ');
3879 if(this.preventDefault){
3882 //this.parent().hideMenuItems();
3884 this.fireEvent('click', this, e);
3903 * @class Roo.bootstrap.MenuSeparator
3904 * @extends Roo.bootstrap.Component
3905 * Bootstrap MenuSeparator class
3908 * Create a new MenuItem
3909 * @param {Object} config The config object
3913 Roo.bootstrap.MenuSeparator = function(config){
3914 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3919 getAutoCreate : function(){
3938 * @class Roo.bootstrap.Modal
3939 * @extends Roo.bootstrap.Component
3940 * Bootstrap Modal class
3941 * @cfg {String} title Title of dialog
3942 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3944 * @cfg {Boolean} specificTitle default false
3945 * @cfg {Array} buttons Array of buttons or standard button set..
3946 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947 * @cfg {Boolean} animate default true
3948 * @cfg {Boolean} allow_close default true
3949 * @cfg {Boolean} fitwindow default false
3950 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3951 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953 * @cfg {String} size (sm|lg|xl) default empty
3954 * @cfg {Number} max_width set the max width of modal
3955 * @cfg {Boolean} editableTitle can the title be edited
3960 * Create a new Modal Dialog
3961 * @param {Object} config The config object
3964 Roo.bootstrap.Modal = function(config){
3965 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3970 * The raw btnclick event for the button
3971 * @param {Roo.EventObject} e
3976 * Fire when dialog resize
3977 * @param {Roo.bootstrap.Modal} this
3978 * @param {Roo.EventObject} e
3982 * @event titlechanged
3983 * Fire when the editable title has been changed
3984 * @param {Roo.bootstrap.Modal} this
3985 * @param {Roo.EventObject} value
3987 "titlechanged" : true
3990 this.buttons = this.buttons || [];
3993 this.tmpl = Roo.factory(this.tmpl);
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4000 title : 'test dialog',
4010 specificTitle: false,
4012 buttonPosition: 'right',
4034 editableTitle : false,
4036 onRender : function(ct, position)
4038 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4041 var cfg = Roo.apply({}, this.getAutoCreate());
4044 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4046 //if (!cfg.name.length) {
4050 cfg.cls += ' ' + this.cls;
4053 cfg.style = this.style;
4055 this.el = Roo.get(document.body).createChild(cfg, position);
4057 //var type = this.el.dom.type;
4060 if(this.tabIndex !== undefined){
4061 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4064 this.dialogEl = this.el.select('.modal-dialog',true).first();
4065 this.bodyEl = this.el.select('.modal-body',true).first();
4066 this.closeEl = this.el.select('.modal-header .close', true).first();
4067 this.headerEl = this.el.select('.modal-header',true).first();
4068 this.titleEl = this.el.select('.modal-title',true).first();
4069 this.footerEl = this.el.select('.modal-footer',true).first();
4071 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4073 //this.el.addClass("x-dlg-modal");
4075 if (this.buttons.length) {
4076 Roo.each(this.buttons, function(bb) {
4077 var b = Roo.apply({}, bb);
4078 b.xns = b.xns || Roo.bootstrap;
4079 b.xtype = b.xtype || 'Button';
4080 if (typeof(b.listeners) == 'undefined') {
4081 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4084 var btn = Roo.factory(b);
4086 btn.render(this.getButtonContainer());
4090 // render the children.
4093 if(typeof(this.items) != 'undefined'){
4094 var items = this.items;
4097 for(var i =0;i < items.length;i++) {
4098 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4102 this.items = nitems;
4104 // where are these used - they used to be body/close/footer
4108 //this.el.addClass([this.fieldClass, this.cls]);
4112 getAutoCreate : function()
4114 // we will default to modal-body-overflow - might need to remove or make optional later.
4116 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4117 html : this.html || ''
4122 cls : 'modal-title',
4126 if(this.specificTitle){ // WTF is this?
4131 if (this.allow_close && Roo.bootstrap.version == 3) {
4141 if (this.editableTitle) {
4143 cls: 'form-control roo-editable-title d-none',
4149 if (this.allow_close && Roo.bootstrap.version == 4) {
4159 if(this.size.length){
4160 size = 'modal-' + this.size;
4163 var footer = Roo.bootstrap.version == 3 ?
4165 cls : 'modal-footer',
4169 cls: 'btn-' + this.buttonPosition
4174 { // BS4 uses mr-auto on left buttons....
4175 cls : 'modal-footer'
4186 cls: "modal-dialog " + size,
4189 cls : "modal-content",
4192 cls : 'modal-header',
4207 modal.cls += ' fade';
4213 getChildContainer : function() {
4218 getButtonContainer : function() {
4220 return Roo.bootstrap.version == 4 ?
4221 this.el.select('.modal-footer',true).first()
4222 : this.el.select('.modal-footer div',true).first();
4225 initEvents : function()
4227 if (this.allow_close) {
4228 this.closeEl.on('click', this.hide, this);
4230 Roo.EventManager.onWindowResize(this.resize, this, true);
4231 if (this.editableTitle) {
4232 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4233 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234 this.headerEditEl.on('keyup', function(e) {
4235 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236 this.toggleHeaderInput(false)
4239 this.headerEditEl.on('blur', function(e) {
4240 this.toggleHeaderInput(false)
4249 this.maskEl.setSize(
4250 Roo.lib.Dom.getViewWidth(true),
4251 Roo.lib.Dom.getViewHeight(true)
4254 if (this.fitwindow) {
4256 this.dialogEl.setStyle( { 'max-width' : '100%' });
4258 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4264 if(this.max_width !== 0) {
4266 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4269 this.setSize(w, this.height);
4273 if(this.max_height) {
4274 this.setSize(w,Math.min(
4276 Roo.lib.Dom.getViewportHeight(true) - 60
4282 if(!this.fit_content) {
4283 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4287 this.setSize(w, Math.min(
4289 this.headerEl.getHeight() +
4290 this.footerEl.getHeight() +
4291 this.getChildHeight(this.bodyEl.dom.childNodes),
4292 Roo.lib.Dom.getViewportHeight(true) - 60)
4298 setSize : function(w,h)
4309 if (!this.rendered) {
4312 this.toggleHeaderInput(false);
4313 //this.el.setStyle('display', 'block');
4314 this.el.removeClass('hideing');
4315 this.el.dom.style.display='block';
4317 Roo.get(document.body).addClass('modal-open');
4319 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4322 this.el.addClass('show');
4323 this.el.addClass('in');
4326 this.el.addClass('show');
4327 this.el.addClass('in');
4330 // not sure how we can show data in here..
4332 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4335 Roo.get(document.body).addClass("x-body-masked");
4337 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4338 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339 this.maskEl.dom.style.display = 'block';
4340 this.maskEl.addClass('show');
4345 this.fireEvent('show', this);
4347 // set zindex here - otherwise it appears to be ignored...
4348 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4351 this.items.forEach( function(e) {
4352 e.layout ? e.layout() : false;
4360 if(this.fireEvent("beforehide", this) !== false){
4362 this.maskEl.removeClass('show');
4364 this.maskEl.dom.style.display = '';
4365 Roo.get(document.body).removeClass("x-body-masked");
4366 this.el.removeClass('in');
4367 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4369 if(this.animate){ // why
4370 this.el.addClass('hideing');
4371 this.el.removeClass('show');
4373 if (!this.el.hasClass('hideing')) {
4374 return; // it's been shown again...
4377 this.el.dom.style.display='';
4379 Roo.get(document.body).removeClass('modal-open');
4380 this.el.removeClass('hideing');
4384 this.el.removeClass('show');
4385 this.el.dom.style.display='';
4386 Roo.get(document.body).removeClass('modal-open');
4389 this.fireEvent('hide', this);
4392 isVisible : function()
4395 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4399 addButton : function(str, cb)
4403 var b = Roo.apply({}, { html : str } );
4404 b.xns = b.xns || Roo.bootstrap;
4405 b.xtype = b.xtype || 'Button';
4406 if (typeof(b.listeners) == 'undefined') {
4407 b.listeners = { click : cb.createDelegate(this) };
4410 var btn = Roo.factory(b);
4412 btn.render(this.getButtonContainer());
4418 setDefaultButton : function(btn)
4420 //this.el.select('.modal-footer').()
4423 resizeTo: function(w,h)
4425 this.dialogEl.setWidth(w);
4427 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4429 this.bodyEl.setHeight(h - diff);
4431 this.fireEvent('resize', this);
4434 setContentSize : function(w, h)
4438 onButtonClick: function(btn,e)
4441 this.fireEvent('btnclick', btn.name, e);
4444 * Set the title of the Dialog
4445 * @param {String} str new Title
4447 setTitle: function(str) {
4448 this.titleEl.dom.innerHTML = str;
4452 * Set the body of the Dialog
4453 * @param {String} str new Title
4455 setBody: function(str) {
4456 this.bodyEl.dom.innerHTML = str;
4459 * Set the body of the Dialog using the template
4460 * @param {Obj} data - apply this data to the template and replace the body contents.
4462 applyBody: function(obj)
4465 Roo.log("Error - using apply Body without a template");
4468 this.tmpl.overwrite(this.bodyEl, obj);
4471 getChildHeight : function(child_nodes)
4475 child_nodes.length == 0
4480 var child_height = 0;
4482 for(var i = 0; i < child_nodes.length; i++) {
4485 * for modal with tabs...
4486 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4488 var layout_childs = child_nodes[i].childNodes;
4490 for(var j = 0; j < layout_childs.length; j++) {
4492 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4494 var layout_body_childs = layout_childs[j].childNodes;
4496 for(var k = 0; k < layout_body_childs.length; k++) {
4498 if(layout_body_childs[k].classList.contains('navbar')) {
4499 child_height += layout_body_childs[k].offsetHeight;
4503 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4505 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4507 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4509 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4525 child_height += child_nodes[i].offsetHeight;
4526 // Roo.log(child_nodes[i].offsetHeight);
4529 return child_height;
4531 toggleHeaderInput : function(is_edit)
4533 if (!this.editableTitle) {
4534 return; // not editable.
4536 if (is_edit && this.is_header_editing) {
4537 return; // already editing..
4541 this.headerEditEl.dom.value = this.title;
4542 this.headerEditEl.removeClass('d-none');
4543 this.headerEditEl.dom.focus();
4544 this.titleEl.addClass('d-none');
4546 this.is_header_editing = true;
4549 // flip back to not editing.
4550 this.title = this.headerEditEl.dom.value;
4551 this.headerEditEl.addClass('d-none');
4552 this.titleEl.removeClass('d-none');
4553 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554 this.is_header_editing = false;
4555 this.fireEvent('titlechanged', this, this.title);
4564 Roo.apply(Roo.bootstrap.Modal, {
4566 * Button config that displays a single OK button
4575 * Button config that displays Yes and No buttons
4591 * Button config that displays OK and Cancel buttons
4606 * Button config that displays Yes, No and Cancel buttons
4631 * messagebox - can be used as a replace
4635 * @class Roo.MessageBox
4636 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4645 // process text value...
4649 // Show a dialog using config options:
4651 title:'Save Changes?',
4652 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653 buttons: Roo.Msg.YESNOCANCEL,
4660 Roo.bootstrap.MessageBox = function(){
4661 var dlg, opt, mask, waitTimer;
4662 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663 var buttons, activeTextEl, bwidth;
4667 var handleButton = function(button){
4669 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4673 var handleHide = function(){
4675 dlg.el.removeClass(opt.cls);
4678 // Roo.TaskMgr.stop(waitTimer);
4679 // waitTimer = null;
4684 var updateButtons = function(b){
4687 buttons["ok"].hide();
4688 buttons["cancel"].hide();
4689 buttons["yes"].hide();
4690 buttons["no"].hide();
4691 dlg.footerEl.hide();
4695 dlg.footerEl.show();
4696 for(var k in buttons){
4697 if(typeof buttons[k] != "function"){
4700 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701 width += buttons[k].el.getWidth()+15;
4711 var handleEsc = function(d, k, e){
4712 if(opt && opt.closable !== false){
4722 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723 * @return {Roo.BasicDialog} The BasicDialog element
4725 getDialog : function(){
4727 dlg = new Roo.bootstrap.Modal( {
4730 //constraintoviewport:false,
4732 //collapsible : false,
4737 //buttonAlign:"center",
4738 closeClick : function(){
4739 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4742 handleButton("cancel");
4747 dlg.on("hide", handleHide);
4749 //dlg.addKeyListener(27, handleEsc);
4751 this.buttons = buttons;
4752 var bt = this.buttonText;
4753 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4758 bodyEl = dlg.bodyEl.createChild({
4760 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761 '<textarea class="roo-mb-textarea"></textarea>' +
4762 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4764 msgEl = bodyEl.dom.firstChild;
4765 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766 textboxEl.enableDisplayMode();
4767 textboxEl.addKeyListener([10,13], function(){
4768 if(dlg.isVisible() && opt && opt.buttons){
4771 }else if(opt.buttons.yes){
4772 handleButton("yes");
4776 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777 textareaEl.enableDisplayMode();
4778 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779 progressEl.enableDisplayMode();
4781 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782 var pf = progressEl.dom.firstChild;
4784 pp = Roo.get(pf.firstChild);
4785 pp.setHeight(pf.offsetHeight);
4793 * Updates the message box body text
4794 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795 * the XHTML-compliant non-breaking space character '&#160;')
4796 * @return {Roo.MessageBox} This message box
4798 updateText : function(text)
4800 if(!dlg.isVisible() && !opt.width){
4801 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4804 msgEl.innerHTML = text || ' ';
4806 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4809 Math.min(opt.width || cw , this.maxWidth),
4810 Math.max(opt.minWidth || this.minWidth, bwidth)
4813 activeTextEl.setWidth(w);
4815 if(dlg.isVisible()){
4816 dlg.fixedcenter = false;
4818 // to big, make it scroll. = But as usual stupid IE does not support
4821 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4825 bodyEl.dom.style.height = '';
4826 bodyEl.dom.style.overflowY = '';
4829 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4831 bodyEl.dom.style.overflowX = '';
4834 dlg.setContentSize(w, bodyEl.getHeight());
4835 if(dlg.isVisible()){
4836 dlg.fixedcenter = true;
4842 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4843 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846 * @return {Roo.MessageBox} This message box
4848 updateProgress : function(value, text){
4850 this.updateText(text);
4853 if (pp) { // weird bug on my firefox - for some reason this is not defined
4854 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4861 * Returns true if the message box is currently displayed
4862 * @return {Boolean} True if the message box is visible, else false
4864 isVisible : function(){
4865 return dlg && dlg.isVisible();
4869 * Hides the message box if it is displayed
4872 if(this.isVisible()){
4878 * Displays a new message box, or reinitializes an existing message box, based on the config options
4879 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880 * The following config object properties are supported:
4882 Property Type Description
4883 ---------- --------------- ------------------------------------------------------------------------------------
4884 animEl String/Element An id or Element from which the message box should animate as it opens and
4885 closes (defaults to undefined)
4886 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable Boolean False to hide the top-right close button (defaults to true). Note that
4889 progress and wait dialogs will ignore this property and always hide the
4890 close button as they can only be closed programmatically.
4891 cls String A custom CSS class to apply to the message box element
4892 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4893 displayed (defaults to 75)
4894 fn Function A callback function to execute after closing the dialog. The arguments to the
4895 function will be btn (the name of the button that was clicked, if applicable,
4896 e.g. "ok"), and text (the value of the active text field, if applicable).
4897 Progress and wait dialogs will ignore this option since they do not respond to
4898 user actions and can only be closed programmatically, so any required function
4899 should be called by the same code after it closes the dialog.
4900 icon String A CSS class that provides a background image to be used as an icon for
4901 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4903 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4904 modal Boolean False to allow user interaction with the page while the message box is
4905 displayed (defaults to true)
4906 msg String A string that will replace the existing message box body text (defaults
4907 to the XHTML-compliant non-breaking space character ' ')
4908 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4909 progress Boolean True to display a progress bar (defaults to false)
4910 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4913 title String The title text
4914 value String The string value to set into the active textbox element if displayed
4915 wait Boolean True to display a progress bar (defaults to false)
4916 width Number The width of the dialog in pixels
4923 msg: 'Please enter your address:',
4925 buttons: Roo.MessageBox.OKCANCEL,
4928 animEl: 'addAddressBtn'
4931 * @param {Object} config Configuration options
4932 * @return {Roo.MessageBox} This message box
4934 show : function(options)
4937 // this causes nightmares if you show one dialog after another
4938 // especially on callbacks..
4940 if(this.isVisible()){
4943 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4945 Roo.log("New Dialog Message:" + options.msg )
4946 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4950 var d = this.getDialog();
4952 d.setTitle(opt.title || " ");
4953 d.closeEl.setDisplayed(opt.closable !== false);
4954 activeTextEl = textboxEl;
4955 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4960 textareaEl.setHeight(typeof opt.multiline == "number" ?
4961 opt.multiline : this.defaultTextHeight);
4962 activeTextEl = textareaEl;
4971 progressEl.setDisplayed(opt.progress === true);
4973 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4975 this.updateProgress(0);
4976 activeTextEl.dom.value = opt.value || "";
4978 dlg.setDefaultButton(activeTextEl);
4980 var bs = opt.buttons;
4984 }else if(bs && bs.yes){
4985 db = buttons["yes"];
4987 dlg.setDefaultButton(db);
4989 bwidth = updateButtons(opt.buttons);
4990 this.updateText(opt.msg);
4992 d.el.addClass(opt.cls);
4994 d.proxyDrag = opt.proxyDrag === true;
4995 d.modal = opt.modal !== false;
4996 d.mask = opt.modal !== false ? mask : false;
4998 // force it to the end of the z-index stack so it gets a cursor in FF
4999 document.body.appendChild(dlg.el.dom);
5000 d.animateTarget = null;
5001 d.show(options.animEl);
5007 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5008 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009 * and closing the message box when the process is complete.
5010 * @param {String} title The title bar text
5011 * @param {String} msg The message box body text
5012 * @return {Roo.MessageBox} This message box
5014 progress : function(title, msg){
5021 minWidth: this.minProgressWidth,
5028 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029 * If a callback function is passed it will be called after the user clicks the button, and the
5030 * id of the button that was clicked will be passed as the only parameter to the callback
5031 * (could also be the top-right close button).
5032 * @param {String} title The title bar text
5033 * @param {String} msg The message box body text
5034 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035 * @param {Object} scope (optional) The scope of the callback function
5036 * @return {Roo.MessageBox} This message box
5038 alert : function(title, msg, fn, scope)
5053 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5054 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055 * You are responsible for closing the message box when the process is complete.
5056 * @param {String} msg The message box body text
5057 * @param {String} title (optional) The title bar text
5058 * @return {Roo.MessageBox} This message box
5060 wait : function(msg, title){
5071 waitTimer = Roo.TaskMgr.start({
5073 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5081 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084 * @param {String} title The title bar text
5085 * @param {String} msg The message box body text
5086 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087 * @param {Object} scope (optional) The scope of the callback function
5088 * @return {Roo.MessageBox} This message box
5090 confirm : function(title, msg, fn, scope){
5094 buttons: this.YESNO,
5103 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5105 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106 * (could also be the top-right close button) and the text that was entered will be passed as the two
5107 * parameters to the callback.
5108 * @param {String} title The title bar text
5109 * @param {String} msg The message box body text
5110 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111 * @param {Object} scope (optional) The scope of the callback function
5112 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114 * @return {Roo.MessageBox} This message box
5116 prompt : function(title, msg, fn, scope, multiline){
5120 buttons: this.OKCANCEL,
5125 multiline: multiline,
5132 * Button config that displays a single OK button
5137 * Button config that displays Yes and No buttons
5140 YESNO : {yes:true, no:true},
5142 * Button config that displays OK and Cancel buttons
5145 OKCANCEL : {ok:true, cancel:true},
5147 * Button config that displays Yes, No and Cancel buttons
5150 YESNOCANCEL : {yes:true, no:true, cancel:true},
5153 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5156 defaultTextHeight : 75,
5158 * The maximum width in pixels of the message box (defaults to 600)
5163 * The minimum width in pixels of the message box (defaults to 100)
5168 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5169 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5172 minProgressWidth : 250,
5174 * An object containing the default button text strings that can be overriden for localized language support.
5175 * Supported properties are: ok, cancel, yes and no.
5176 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5189 * Shorthand for {@link Roo.MessageBox}
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5201 * @class Roo.bootstrap.Navbar
5202 * @extends Roo.bootstrap.Component
5203 * Bootstrap Navbar class
5206 * Create a new Navbar
5207 * @param {Object} config The config object
5211 Roo.bootstrap.Navbar = function(config){
5212 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5216 * @event beforetoggle
5217 * Fire before toggle the menu
5218 * @param {Roo.EventObject} e
5220 "beforetoggle" : true
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5233 getAutoCreate : function(){
5236 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5240 initEvents :function ()
5242 //Roo.log(this.el.select('.navbar-toggle',true));
5243 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5250 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5252 var size = this.el.getSize();
5253 this.maskEl.setSize(size.width, size.height);
5254 this.maskEl.enableDisplayMode("block");
5263 getChildContainer : function()
5265 if (this.el && this.el.select('.collapse').getCount()) {
5266 return this.el.select('.collapse',true).first();
5281 onToggle : function()
5284 if(this.fireEvent('beforetoggle', this) === false){
5287 var ce = this.el.select('.navbar-collapse',true).first();
5289 if (!ce.hasClass('show')) {
5299 * Expand the navbar pulldown
5301 expand : function ()
5304 var ce = this.el.select('.navbar-collapse',true).first();
5305 if (ce.hasClass('collapsing')) {
5308 ce.dom.style.height = '';
5310 ce.addClass('in'); // old...
5311 ce.removeClass('collapse');
5312 ce.addClass('show');
5313 var h = ce.getHeight();
5315 ce.removeClass('show');
5316 // at this point we should be able to see it..
5317 ce.addClass('collapsing');
5319 ce.setHeight(0); // resize it ...
5320 ce.on('transitionend', function() {
5321 //Roo.log('done transition');
5322 ce.removeClass('collapsing');
5323 ce.addClass('show');
5324 ce.removeClass('collapse');
5326 ce.dom.style.height = '';
5327 }, this, { single: true} );
5329 ce.dom.scrollTop = 0;
5332 * Collapse the navbar pulldown
5334 collapse : function()
5336 var ce = this.el.select('.navbar-collapse',true).first();
5338 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339 // it's collapsed or collapsing..
5342 ce.removeClass('in'); // old...
5343 ce.setHeight(ce.getHeight());
5344 ce.removeClass('show');
5345 ce.addClass('collapsing');
5347 ce.on('transitionend', function() {
5348 ce.dom.style.height = '';
5349 ce.removeClass('collapsing');
5350 ce.addClass('collapse');
5351 }, this, { single: true} );
5371 * @class Roo.bootstrap.NavSimplebar
5372 * @extends Roo.bootstrap.Navbar
5373 * Bootstrap Sidebar class
5375 * @cfg {Boolean} inverse is inverted color
5377 * @cfg {String} type (nav | pills | tabs)
5378 * @cfg {Boolean} arrangement stacked | justified
5379 * @cfg {String} align (left | right) alignment
5381 * @cfg {Boolean} main (true|false) main nav bar? default false
5382 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5384 * @cfg {String} tag (header|footer|nav|div) default is nav
5386 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5390 * Create a new Sidebar
5391 * @param {Object} config The config object
5395 Roo.bootstrap.NavSimplebar = function(config){
5396 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5415 getAutoCreate : function(){
5419 tag : this.tag || 'div',
5420 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5422 if (['light','white'].indexOf(this.weight) > -1) {
5423 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5425 cfg.cls += ' bg-' + this.weight;
5428 cfg.cls += ' navbar-inverse';
5432 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5434 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5443 cls: 'nav nav-' + this.xtype,
5449 this.type = this.type || 'nav';
5450 if (['tabs','pills'].indexOf(this.type) != -1) {
5451 cfg.cn[0].cls += ' nav-' + this.type
5455 if (this.type!=='nav') {
5456 Roo.log('nav type must be nav/tabs/pills')
5458 cfg.cn[0].cls += ' navbar-nav'
5464 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465 cfg.cn[0].cls += ' nav-' + this.arrangement;
5469 if (this.align === 'right') {
5470 cfg.cn[0].cls += ' navbar-right';
5495 * navbar-expand-md fixed-top
5499 * @class Roo.bootstrap.NavHeaderbar
5500 * @extends Roo.bootstrap.NavSimplebar
5501 * Bootstrap Sidebar class
5503 * @cfg {String} brand what is brand
5504 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505 * @cfg {String} brand_href href of the brand
5506 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5507 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5512 * Create a new Sidebar
5513 * @param {Object} config The config object
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5529 desktopCenter : false,
5532 getAutoCreate : function(){
5535 tag: this.nav || 'nav',
5536 cls: 'navbar navbar-expand-md',
5542 if (this.desktopCenter) {
5543 cn.push({cls : 'container', cn : []});
5551 cls: 'navbar-toggle navbar-toggler',
5552 'data-toggle': 'collapse',
5557 html: 'Toggle navigation'
5561 cls: 'icon-bar navbar-toggler-icon'
5574 cn.push( Roo.bootstrap.version == 4 ? btn : {
5576 cls: 'navbar-header',
5585 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5589 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5591 if (['light','white'].indexOf(this.weight) > -1) {
5592 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5594 cfg.cls += ' bg-' + this.weight;
5597 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5600 // tag can override this..
5602 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5605 if (this.brand !== '') {
5606 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5609 href: this.brand_href ? this.brand_href : '#',
5610 cls: 'navbar-brand',
5618 cfg.cls += ' main-nav';
5626 getHeaderChildContainer : function()
5628 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629 return this.el.select('.navbar-header',true).first();
5632 return this.getChildContainer();
5635 getChildContainer : function()
5638 return this.el.select('.roo-navbar-collapse',true).first();
5643 initEvents : function()
5645 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5647 if (this.autohide) {
5652 Roo.get(document).on('scroll',function(e) {
5653 var ns = Roo.get(document).getScroll().top;
5654 var os = prevScroll;
5658 ft.removeClass('slideDown');
5659 ft.addClass('slideUp');
5662 ft.removeClass('slideUp');
5663 ft.addClass('slideDown');
5684 * @class Roo.bootstrap.NavSidebar
5685 * @extends Roo.bootstrap.Navbar
5686 * Bootstrap Sidebar class
5689 * Create a new Sidebar
5690 * @param {Object} config The config object
5694 Roo.bootstrap.NavSidebar = function(config){
5695 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5700 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5702 getAutoCreate : function(){
5707 cls: 'sidebar sidebar-nav'
5729 * @class Roo.bootstrap.NavGroup
5730 * @extends Roo.bootstrap.Component
5731 * Bootstrap NavGroup class
5732 * @cfg {String} align (left|right)
5733 * @cfg {Boolean} inverse
5734 * @cfg {String} type (nav|pills|tab) default nav
5735 * @cfg {String} navId - reference Id for navbar.
5736 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5739 * Create a new nav group
5740 * @param {Object} config The config object
5743 Roo.bootstrap.NavGroup = function(config){
5744 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5747 Roo.bootstrap.NavGroup.register(this);
5751 * Fires when the active item changes
5752 * @param {Roo.bootstrap.NavGroup} this
5753 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5773 getAutoCreate : function()
5775 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5781 if (Roo.bootstrap.version == 4) {
5782 if (['tabs','pills'].indexOf(this.type) != -1) {
5783 cfg.cls += ' nav-' + this.type;
5785 // trying to remove so header bar can right align top?
5786 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787 // do not use on header bar...
5788 cfg.cls += ' navbar-nav';
5793 if (['tabs','pills'].indexOf(this.type) != -1) {
5794 cfg.cls += ' nav-' + this.type
5796 if (this.type !== 'nav') {
5797 Roo.log('nav type must be nav/tabs/pills')
5799 cfg.cls += ' navbar-nav'
5803 if (this.parent() && this.parent().sidebar) {
5806 cls: 'dashboard-menu sidebar-menu'
5812 if (this.form === true) {
5815 cls: 'navbar-form form-inline'
5817 //nav navbar-right ml-md-auto
5818 if (this.align === 'right') {
5819 cfg.cls += ' navbar-right ml-md-auto';
5821 cfg.cls += ' navbar-left';
5825 if (this.align === 'right') {
5826 cfg.cls += ' navbar-right ml-md-auto';
5828 cfg.cls += ' mr-auto';
5832 cfg.cls += ' navbar-inverse';
5840 * sets the active Navigation item
5841 * @param {Roo.bootstrap.NavItem} the new current navitem
5843 setActiveItem : function(item)
5846 Roo.each(this.navItems, function(v){
5851 v.setActive(false, true);
5858 item.setActive(true, true);
5859 this.fireEvent('changed', this, item, prev);
5864 * gets the active Navigation item
5865 * @return {Roo.bootstrap.NavItem} the current navitem
5867 getActive : function()
5871 Roo.each(this.navItems, function(v){
5882 indexOfNav : function()
5886 Roo.each(this.navItems, function(v,i){
5897 * adds a Navigation item
5898 * @param {Roo.bootstrap.NavItem} the navitem to add
5900 addItem : function(cfg)
5902 if (this.form && Roo.bootstrap.version == 4) {
5905 var cn = new Roo.bootstrap.NavItem(cfg);
5907 cn.parentId = this.id;
5908 cn.onRender(this.el, null);
5912 * register a Navigation item
5913 * @param {Roo.bootstrap.NavItem} the navitem to add
5915 register : function(item)
5917 this.navItems.push( item);
5918 item.navId = this.navId;
5923 * clear all the Navigation item
5926 clearAll : function()
5929 this.el.dom.innerHTML = '';
5932 getNavItem: function(tabId)
5935 Roo.each(this.navItems, function(e) {
5936 if (e.tabId == tabId) {
5946 setActiveNext : function()
5948 var i = this.indexOfNav(this.getActive());
5949 if (i > this.navItems.length) {
5952 this.setActiveItem(this.navItems[i+1]);
5954 setActivePrev : function()
5956 var i = this.indexOfNav(this.getActive());
5960 this.setActiveItem(this.navItems[i-1]);
5962 clearWasActive : function(except) {
5963 Roo.each(this.navItems, function(e) {
5964 if (e.tabId != except.tabId && e.was_active) {
5965 e.was_active = false;
5972 getWasActive : function ()
5975 Roo.each(this.navItems, function(e) {
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5994 * register a Navigation Group
5995 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5997 register : function(navgrp)
5999 this.groups[navgrp.navId] = navgrp;
6003 * fetch a Navigation Group based on the navigation ID
6004 * @param {string} the navgroup to add
6005 * @returns {Roo.bootstrap.NavGroup} the navgroup
6007 get: function(navId) {
6008 if (typeof(this.groups[navId]) == 'undefined') {
6010 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6012 return this.groups[navId] ;
6027 * @class Roo.bootstrap.NavItem
6028 * @extends Roo.bootstrap.Component
6029 * Bootstrap Navbar.NavItem class
6030 * @cfg {String} href link to
6031 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032 * @cfg {Boolean} button_outline show and outlined button
6033 * @cfg {String} html content of button
6034 * @cfg {String} badge text inside badge
6035 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036 * @cfg {String} glyphicon DEPRICATED - use fa
6037 * @cfg {String} icon DEPRICATED - use fa
6038 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039 * @cfg {Boolean} active Is item active
6040 * @cfg {Boolean} disabled Is item disabled
6041 * @cfg {String} linkcls Link Class
6042 * @cfg {Boolean} preventDefault (true | false) default false
6043 * @cfg {String} tabId the tab that this item activates.
6044 * @cfg {String} tagtype (a|span) render as a href or span?
6045 * @cfg {Boolean} animateRef (true|false) link to element default false
6048 * Create a new Navbar Item
6049 * @param {Object} config The config object
6051 Roo.bootstrap.NavItem = function(config){
6052 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6057 * The raw click event for the entire grid.
6058 * @param {Roo.EventObject} e
6063 * Fires when the active item active state changes
6064 * @param {Roo.bootstrap.NavItem} this
6065 * @param {boolean} state the new state
6071 * Fires when scroll to element
6072 * @param {Roo.bootstrap.NavItem} this
6073 * @param {Object} options
6074 * @param {Roo.EventObject} e
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6091 preventDefault : false,
6099 button_outline : false,
6103 getAutoCreate : function(){
6110 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6113 cfg.cls += ' active' ;
6115 if (this.disabled) {
6116 cfg.cls += ' disabled';
6120 if (this.button_weight.length) {
6121 cfg.tag = this.href ? 'a' : 'button';
6122 cfg.html = this.html || '';
6123 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6125 cfg.href = this.href;
6128 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6131 // menu .. should add dropdown-menu class - so no need for carat..
6133 if (this.badge !== '') {
6135 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6140 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6144 href : this.href || "#",
6145 html: this.html || ''
6148 if (this.tagtype == 'a') {
6149 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6153 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6156 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6158 if(this.glyphicon) {
6159 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6164 cfg.cn[0].html += " <span class='caret'></span>";
6168 if (this.badge !== '') {
6170 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6178 onRender : function(ct, position)
6180 // Roo.log("Call onRender: " + this.xtype);
6181 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6185 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186 this.navLink = this.el.select('.nav-link',true).first();
6191 initEvents: function()
6193 if (typeof (this.menu) != 'undefined') {
6194 this.menu.parentType = this.xtype;
6195 this.menu.triggerEl = this.el;
6196 this.menu = this.addxtype(Roo.apply({}, this.menu));
6199 this.el.on('click', this.onClick, this);
6201 //if(this.tagtype == 'span'){
6202 // this.el.select('span',true).on('click', this.onClick, this);
6205 // at this point parent should be available..
6206 this.parent().register(this);
6209 onClick : function(e)
6211 if (e.getTarget('.dropdown-menu-item')) {
6212 // did you click on a menu itemm.... - then don't trigger onclick..
6217 this.preventDefault ||
6220 Roo.log("NavItem - prevent Default?");
6224 if (this.disabled) {
6228 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229 if (tg && tg.transition) {
6230 Roo.log("waiting for the transitionend");
6236 //Roo.log("fire event clicked");
6237 if(this.fireEvent('click', this, e) === false){
6241 if(this.tagtype == 'span'){
6245 //Roo.log(this.href);
6246 var ael = this.el.select('a',true).first();
6249 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252 return; // ignore... - it's a 'hash' to another page.
6254 Roo.log("NavItem - prevent Default?");
6256 this.scrollToElement(e);
6260 var p = this.parent();
6262 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263 if (typeof(p.setActiveItem) !== 'undefined') {
6264 p.setActiveItem(this);
6268 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270 // remove the collapsed menu expand...
6271 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6275 isActive: function () {
6278 setActive : function(state, fire, is_was_active)
6280 if (this.active && !state && this.navId) {
6281 this.was_active = true;
6282 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6284 nv.clearWasActive(this);
6288 this.active = state;
6291 this.el.removeClass('active');
6292 this.navLink ? this.navLink.removeClass('active') : false;
6293 } else if (!this.el.hasClass('active')) {
6295 this.el.addClass('active');
6296 if (Roo.bootstrap.version == 4 && this.navLink ) {
6297 this.navLink.addClass('active');
6302 this.fireEvent('changed', this, state);
6305 // show a panel if it's registered and related..
6307 if (!this.navId || !this.tabId || !state || is_was_active) {
6311 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6315 var pan = tg.getPanelByName(this.tabId);
6319 // if we can not flip to new panel - go back to old nav highlight..
6320 if (false == tg.showPanel(pan)) {
6321 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6323 var onav = nv.getWasActive();
6325 onav.setActive(true, false, true);
6334 // this should not be here...
6335 setDisabled : function(state)
6337 this.disabled = state;
6339 this.el.removeClass('disabled');
6340 } else if (!this.el.hasClass('disabled')) {
6341 this.el.addClass('disabled');
6347 * Fetch the element to display the tooltip on.
6348 * @return {Roo.Element} defaults to this.el
6350 tooltipEl : function()
6352 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6355 scrollToElement : function(e)
6357 var c = document.body;
6360 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6362 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363 c = document.documentElement;
6366 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6372 var o = target.calcOffsetsTo(c);
6379 this.fireEvent('scrollto', this, options, e);
6381 Roo.get(c).scrollTo('top', options.value, true);
6394 * <span> icon </span>
6395 * <span> text </span>
6396 * <span>badge </span>
6400 * @class Roo.bootstrap.NavSidebarItem
6401 * @extends Roo.bootstrap.NavItem
6402 * Bootstrap Navbar.NavSidebarItem class
6403 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404 * {Boolean} open is the menu open
6405 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407 * {String} buttonSize (sm|md|lg)the extra classes for the button
6408 * {Boolean} showArrow show arrow next to the text (default true)
6410 * Create a new Navbar Button
6411 * @param {Object} config The config object
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6419 * The raw click event for the entire grid.
6420 * @param {Roo.EventObject} e
6425 * Fires when the active item active state changes
6426 * @param {Roo.bootstrap.NavSidebarItem} this
6427 * @param {boolean} state the new state
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6437 badgeWeight : 'default',
6443 buttonWeight : 'default',
6449 getAutoCreate : function(){
6454 href : this.href || '#',
6460 if(this.buttonView){
6463 href : this.href || '#',
6464 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6477 cfg.cls += ' active';
6480 if (this.disabled) {
6481 cfg.cls += ' disabled';
6484 cfg.cls += ' open x-open';
6487 if (this.glyphicon || this.icon) {
6488 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6489 a.cn.push({ tag : 'i', cls : c }) ;
6492 if(!this.buttonView){
6495 html : this.html || ''
6502 if (this.badge !== '') {
6503 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6509 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6512 a.cls += ' dropdown-toggle treeview' ;
6518 initEvents : function()
6520 if (typeof (this.menu) != 'undefined') {
6521 this.menu.parentType = this.xtype;
6522 this.menu.triggerEl = this.el;
6523 this.menu = this.addxtype(Roo.apply({}, this.menu));
6526 this.el.on('click', this.onClick, this);
6528 if(this.badge !== ''){
6529 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6534 onClick : function(e)
6541 if(this.preventDefault){
6545 this.fireEvent('click', this, e);
6548 disable : function()
6550 this.setDisabled(true);
6555 this.setDisabled(false);
6558 setDisabled : function(state)
6560 if(this.disabled == state){
6564 this.disabled = state;
6567 this.el.addClass('disabled');
6571 this.el.removeClass('disabled');
6576 setActive : function(state)
6578 if(this.active == state){
6582 this.active = state;
6585 this.el.addClass('active');
6589 this.el.removeClass('active');
6594 isActive: function ()
6599 setBadge : function(str)
6605 this.badgeEl.dom.innerHTML = str;
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6624 * @class Roo.bootstrap.breadcrumb.Nav
6625 * @extends Roo.bootstrap.Component
6626 * Bootstrap Breadcrumb Nav Class
6628 * @children Roo.bootstrap.breadcrumb.Item
6631 * Create a new breadcrumb.Nav
6632 * @param {Object} config The config object
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6644 getAutoCreate : function()
6661 initEvents: function()
6663 this.olEl = this.el.select('ol',true).first();
6665 getChildContainer : function()
6681 * @class Roo.bootstrap.breadcrumb.Nav
6682 * @extends Roo.bootstrap.Component
6683 * Bootstrap Breadcrumb Nav Class
6685 * @children Roo.bootstrap.breadcrumb.Component
6686 * @cfg {String} html the content of the link.
6687 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688 * @cfg {Boolean} active is it active
6692 * Create a new breadcrumb.Nav
6693 * @param {Object} config The config object
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6702 * The img click event for the img.
6703 * @param {Roo.EventObject} e
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6715 getAutoCreate : function()
6720 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6722 if (this.href !== false) {
6729 cfg.html = this.html;
6735 initEvents: function()
6738 this.el.select('a', true).first().on('click',this.onClick, this)
6742 onClick : function(e)
6745 this.fireEvent('click',this, e);
6758 * @class Roo.bootstrap.Row
6759 * @extends Roo.bootstrap.Component
6760 * Bootstrap Row class (contains columns...)
6764 * @param {Object} config The config object
6767 Roo.bootstrap.Row = function(config){
6768 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6773 getAutoCreate : function(){
6792 * @class Roo.bootstrap.Pagination
6793 * @extends Roo.bootstrap.Component
6794 * Bootstrap Pagination class
6795 * @cfg {String} size xs | sm | md | lg
6796 * @cfg {Boolean} inverse false | true
6799 * Create a new Pagination
6800 * @param {Object} config The config object
6803 Roo.bootstrap.Pagination = function(config){
6804 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6813 getAutoCreate : function(){
6819 cfg.cls += ' inverse';
6825 cfg.cls += " " + this.cls;
6843 * @class Roo.bootstrap.PaginationItem
6844 * @extends Roo.bootstrap.Component
6845 * Bootstrap PaginationItem class
6846 * @cfg {String} html text
6847 * @cfg {String} href the link
6848 * @cfg {Boolean} preventDefault (true | false) default true
6849 * @cfg {Boolean} active (true | false) default false
6850 * @cfg {Boolean} disabled default false
6854 * Create a new PaginationItem
6855 * @param {Object} config The config object
6859 Roo.bootstrap.PaginationItem = function(config){
6860 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6865 * The raw click event for the entire grid.
6866 * @param {Roo.EventObject} e
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6876 preventDefault: true,
6881 getAutoCreate : function(){
6887 href : this.href ? this.href : '#',
6888 html : this.html ? this.html : ''
6898 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6902 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6908 initEvents: function() {
6910 this.el.on('click', this.onClick, this);
6913 onClick : function(e)
6915 Roo.log('PaginationItem on click ');
6916 if(this.preventDefault){
6924 this.fireEvent('click', this, e);
6940 * @class Roo.bootstrap.Slider
6941 * @extends Roo.bootstrap.Component
6942 * Bootstrap Slider class
6945 * Create a new Slider
6946 * @param {Object} config The config object
6949 Roo.bootstrap.Slider = function(config){
6950 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6955 getAutoCreate : function(){
6959 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6963 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6975 * Ext JS Library 1.1.1
6976 * Copyright(c) 2006-2007, Ext JS, LLC.
6978 * Originally Released Under LGPL - original licence link has changed is not relivant.
6981 * <script type="text/javascript">
6986 * @class Roo.grid.ColumnModel
6987 * @extends Roo.util.Observable
6988 * This is the default implementation of a ColumnModel used by the Grid. It defines
6989 * the columns in the grid.
6992 var colModel = new Roo.grid.ColumnModel([
6993 {header: "Ticker", width: 60, sortable: true, locked: true},
6994 {header: "Company Name", width: 150, sortable: true},
6995 {header: "Market Cap.", width: 100, sortable: true},
6996 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997 {header: "Employees", width: 100, sortable: true, resizable: false}
7002 * The config options listed for this class are options which may appear in each
7003 * individual column definition.
7004 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7006 * @param {Object} config An Array of column config objects. See this class's
7007 * config objects for details.
7009 Roo.grid.ColumnModel = function(config){
7011 * The config passed into the constructor
7013 this.config = config;
7016 // if no id, create one
7017 // if the column does not have a dataIndex mapping,
7018 // map it to the order it is in the config
7019 for(var i = 0, len = config.length; i < len; i++){
7021 if(typeof c.dataIndex == "undefined"){
7024 if(typeof c.renderer == "string"){
7025 c.renderer = Roo.util.Format[c.renderer];
7027 if(typeof c.id == "undefined"){
7030 if(c.editor && c.editor.xtype){
7031 c.editor = Roo.factory(c.editor, Roo.grid);
7033 if(c.editor && c.editor.isFormField){
7034 c.editor = new Roo.grid.GridEditor(c.editor);
7036 this.lookup[c.id] = c;
7040 * The width of columns which have no width specified (defaults to 100)
7043 this.defaultWidth = 100;
7046 * Default sortable of columns which have no sortable specified (defaults to false)
7049 this.defaultSortable = false;
7053 * @event widthchange
7054 * Fires when the width of a column changes.
7055 * @param {ColumnModel} this
7056 * @param {Number} columnIndex The column index
7057 * @param {Number} newWidth The new width
7059 "widthchange": true,
7061 * @event headerchange
7062 * Fires when the text of a header changes.
7063 * @param {ColumnModel} this
7064 * @param {Number} columnIndex The column index
7065 * @param {Number} newText The new header text
7067 "headerchange": true,
7069 * @event hiddenchange
7070 * Fires when a column is hidden or "unhidden".
7071 * @param {ColumnModel} this
7072 * @param {Number} columnIndex The column index
7073 * @param {Boolean} hidden true if hidden, false otherwise
7075 "hiddenchange": true,
7077 * @event columnmoved
7078 * Fires when a column is moved.
7079 * @param {ColumnModel} this
7080 * @param {Number} oldIndex
7081 * @param {Number} newIndex
7083 "columnmoved" : true,
7085 * @event columlockchange
7086 * Fires when a column's locked state is changed
7087 * @param {ColumnModel} this
7088 * @param {Number} colIndex
7089 * @param {Boolean} locked true if locked
7091 "columnlockchange" : true
7093 Roo.grid.ColumnModel.superclass.constructor.call(this);
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7097 * @cfg {String} header The header text to display in the Grid view.
7100 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102 * specified, the column's index is used as an index into the Record's data Array.
7105 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7109 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110 * Defaults to the value of the {@link #defaultSortable} property.
7111 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7114 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7117 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7120 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7123 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7126 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7132 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7135 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7138 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7141 * @cfg {String} cursor (Optional)
7144 * @cfg {String} tooltip (Optional)
7147 * @cfg {Number} xs (Optional)
7150 * @cfg {Number} sm (Optional)
7153 * @cfg {Number} md (Optional)
7156 * @cfg {Number} lg (Optional)
7159 * Returns the id of the column at the specified index.
7160 * @param {Number} index The column index
7161 * @return {String} the id
7163 getColumnId : function(index){
7164 return this.config[index].id;
7168 * Returns the column for a specified id.
7169 * @param {String} id The column id
7170 * @return {Object} the column
7172 getColumnById : function(id){
7173 return this.lookup[id];
7178 * Returns the column for a specified dataIndex.
7179 * @param {String} dataIndex The column dataIndex
7180 * @return {Object|Boolean} the column or false if not found
7182 getColumnByDataIndex: function(dataIndex){
7183 var index = this.findColumnIndex(dataIndex);
7184 return index > -1 ? this.config[index] : false;
7188 * Returns the index for a specified column id.
7189 * @param {String} id The column id
7190 * @return {Number} the index, or -1 if not found
7192 getIndexById : function(id){
7193 for(var i = 0, len = this.config.length; i < len; i++){
7194 if(this.config[i].id == id){
7202 * Returns the index for a specified column dataIndex.
7203 * @param {String} dataIndex The column dataIndex
7204 * @return {Number} the index, or -1 if not found
7207 findColumnIndex : function(dataIndex){
7208 for(var i = 0, len = this.config.length; i < len; i++){
7209 if(this.config[i].dataIndex == dataIndex){
7217 moveColumn : function(oldIndex, newIndex){
7218 var c = this.config[oldIndex];
7219 this.config.splice(oldIndex, 1);
7220 this.config.splice(newIndex, 0, c);
7221 this.dataMap = null;
7222 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7225 isLocked : function(colIndex){
7226 return this.config[colIndex].locked === true;
7229 setLocked : function(colIndex, value, suppressEvent){
7230 if(this.isLocked(colIndex) == value){
7233 this.config[colIndex].locked = value;
7235 this.fireEvent("columnlockchange", this, colIndex, value);
7239 getTotalLockedWidth : function(){
7241 for(var i = 0; i < this.config.length; i++){
7242 if(this.isLocked(i) && !this.isHidden(i)){
7243 this.totalWidth += this.getColumnWidth(i);
7249 getLockedCount : function(){
7250 for(var i = 0, len = this.config.length; i < len; i++){
7251 if(!this.isLocked(i)){
7256 return this.config.length;
7260 * Returns the number of columns.
7263 getColumnCount : function(visibleOnly){
7264 if(visibleOnly === true){
7266 for(var i = 0, len = this.config.length; i < len; i++){
7267 if(!this.isHidden(i)){
7273 return this.config.length;
7277 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278 * @param {Function} fn
7279 * @param {Object} scope (optional)
7280 * @return {Array} result
7282 getColumnsBy : function(fn, scope){
7284 for(var i = 0, len = this.config.length; i < len; i++){
7285 var c = this.config[i];
7286 if(fn.call(scope||this, c, i) === true){
7294 * Returns true if the specified column is sortable.
7295 * @param {Number} col The column index
7298 isSortable : function(col){
7299 if(typeof this.config[col].sortable == "undefined"){
7300 return this.defaultSortable;
7302 return this.config[col].sortable;
7306 * Returns the rendering (formatting) function defined for the column.
7307 * @param {Number} col The column index.
7308 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7310 getRenderer : function(col){
7311 if(!this.config[col].renderer){
7312 return Roo.grid.ColumnModel.defaultRenderer;
7314 return this.config[col].renderer;
7318 * Sets the rendering (formatting) function for a column.
7319 * @param {Number} col The column index
7320 * @param {Function} fn The function to use to process the cell's raw data
7321 * to return HTML markup for the grid view. The render function is called with
7322 * the following parameters:<ul>
7323 * <li>Data value.</li>
7324 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325 * <li>css A CSS style string to apply to the table cell.</li>
7326 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328 * <li>Row index</li>
7329 * <li>Column index</li>
7330 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7332 setRenderer : function(col, fn){
7333 this.config[col].renderer = fn;
7337 * Returns the width for the specified column.
7338 * @param {Number} col The column index
7341 getColumnWidth : function(col){
7342 return this.config[col].width * 1 || this.defaultWidth;
7346 * Sets the width for a column.
7347 * @param {Number} col The column index
7348 * @param {Number} width The new width
7350 setColumnWidth : function(col, width, suppressEvent){
7351 this.config[col].width = width;
7352 this.totalWidth = null;
7354 this.fireEvent("widthchange", this, col, width);
7359 * Returns the total width of all columns.
7360 * @param {Boolean} includeHidden True to include hidden column widths
7363 getTotalWidth : function(includeHidden){
7364 if(!this.totalWidth){
7365 this.totalWidth = 0;
7366 for(var i = 0, len = this.config.length; i < len; i++){
7367 if(includeHidden || !this.isHidden(i)){
7368 this.totalWidth += this.getColumnWidth(i);
7372 return this.totalWidth;
7376 * Returns the header for the specified column.
7377 * @param {Number} col The column index
7380 getColumnHeader : function(col){
7381 return this.config[col].header;
7385 * Sets the header for a column.
7386 * @param {Number} col The column index
7387 * @param {String} header The new header
7389 setColumnHeader : function(col, header){
7390 this.config[col].header = header;
7391 this.fireEvent("headerchange", this, col, header);
7395 * Returns the tooltip for the specified column.
7396 * @param {Number} col The column index
7399 getColumnTooltip : function(col){
7400 return this.config[col].tooltip;
7403 * Sets the tooltip for a column.
7404 * @param {Number} col The column index
7405 * @param {String} tooltip The new tooltip
7407 setColumnTooltip : function(col, tooltip){
7408 this.config[col].tooltip = tooltip;
7412 * Returns the dataIndex for the specified column.
7413 * @param {Number} col The column index
7416 getDataIndex : function(col){
7417 return this.config[col].dataIndex;
7421 * Sets the dataIndex for a column.
7422 * @param {Number} col The column index
7423 * @param {Number} dataIndex The new dataIndex
7425 setDataIndex : function(col, dataIndex){
7426 this.config[col].dataIndex = dataIndex;
7432 * Returns true if the cell is editable.
7433 * @param {Number} colIndex The column index
7434 * @param {Number} rowIndex The row index - this is nto actually used..?
7437 isCellEditable : function(colIndex, rowIndex){
7438 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7442 * Returns the editor defined for the cell/column.
7443 * return false or null to disable editing.
7444 * @param {Number} colIndex The column index
7445 * @param {Number} rowIndex The row index
7448 getCellEditor : function(colIndex, rowIndex){
7449 return this.config[colIndex].editor;
7453 * Sets if a column is editable.
7454 * @param {Number} col The column index
7455 * @param {Boolean} editable True if the column is editable
7457 setEditable : function(col, editable){
7458 this.config[col].editable = editable;
7463 * Returns true if the column is hidden.
7464 * @param {Number} colIndex The column index
7467 isHidden : function(colIndex){
7468 return this.config[colIndex].hidden;
7473 * Returns true if the column width cannot be changed
7475 isFixed : function(colIndex){
7476 return this.config[colIndex].fixed;
7480 * Returns true if the column can be resized
7483 isResizable : function(colIndex){
7484 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7487 * Sets if a column is hidden.
7488 * @param {Number} colIndex The column index
7489 * @param {Boolean} hidden True if the column is hidden
7491 setHidden : function(colIndex, hidden){
7492 this.config[colIndex].hidden = hidden;
7493 this.totalWidth = null;
7494 this.fireEvent("hiddenchange", this, colIndex, hidden);
7498 * Sets the editor for a column.
7499 * @param {Number} col The column index
7500 * @param {Object} editor The editor object
7502 setEditor : function(col, editor){
7503 this.config[col].editor = editor;
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7509 if(typeof value == "object") {
7512 if(typeof value == "string" && value.length < 1){
7516 return String.format("{0}", value);
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7523 * Ext JS Library 1.1.1
7524 * Copyright(c) 2006-2007, Ext JS, LLC.
7526 * Originally Released Under LGPL - original licence link has changed is not relivant.
7529 * <script type="text/javascript">
7533 * @class Roo.LoadMask
7534 * A simple utility class for generically masking elements while loading data. If the element being masked has
7535 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7537 * element's UpdateManager load indicator and will be destroyed after the initial load.
7539 * Create a new LoadMask
7540 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541 * @param {Object} config The config object
7543 Roo.LoadMask = function(el, config){
7544 this.el = Roo.get(el);
7545 Roo.apply(this, config);
7547 this.store.on('beforeload', this.onBeforeLoad, this);
7548 this.store.on('load', this.onLoad, this);
7549 this.store.on('loadexception', this.onLoadException, this);
7550 this.removeMask = false;
7552 var um = this.el.getUpdateManager();
7553 um.showLoadIndicator = false; // disable the default indicator
7554 um.on('beforeupdate', this.onBeforeLoad, this);
7555 um.on('update', this.onLoad, this);
7556 um.on('failure', this.onLoad, this);
7557 this.removeMask = true;
7561 Roo.LoadMask.prototype = {
7563 * @cfg {Boolean} removeMask
7564 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7569 * The text to display in a centered loading message box (defaults to 'Loading...')
7573 * @cfg {String} msgCls
7574 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7576 msgCls : 'x-mask-loading',
7579 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7585 * Disables the mask to prevent it from being displayed
7587 disable : function(){
7588 this.disabled = true;
7592 * Enables the mask so that it can be displayed
7594 enable : function(){
7595 this.disabled = false;
7598 onLoadException : function()
7602 if (typeof(arguments[3]) != 'undefined') {
7603 Roo.MessageBox.alert("Error loading",arguments[3]);
7607 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7615 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7620 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7624 onBeforeLoad : function(){
7626 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7631 destroy : function(){
7633 this.store.un('beforeload', this.onBeforeLoad, this);
7634 this.store.un('load', this.onLoad, this);
7635 this.store.un('loadexception', this.onLoadException, this);
7637 var um = this.el.getUpdateManager();
7638 um.un('beforeupdate', this.onBeforeLoad, this);
7639 um.un('update', this.onLoad, this);
7640 um.un('failure', this.onLoad, this);
7651 * @class Roo.bootstrap.Table
7652 * @extends Roo.bootstrap.Component
7653 * Bootstrap Table class
7654 * @cfg {String} cls table class
7655 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656 * @cfg {String} bgcolor Specifies the background color for a table
7657 * @cfg {Number} border Specifies whether the table cells should have borders or not
7658 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659 * @cfg {Number} cellspacing Specifies the space between cells
7660 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662 * @cfg {String} sortable Specifies that the table should be sortable
7663 * @cfg {String} summary Specifies a summary of the content of a table
7664 * @cfg {Number} width Specifies the width of a table
7665 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7667 * @cfg {boolean} striped Should the rows be alternative striped
7668 * @cfg {boolean} bordered Add borders to the table
7669 * @cfg {boolean} hover Add hover highlighting
7670 * @cfg {boolean} condensed Format condensed
7671 * @cfg {boolean} responsive Format condensed
7672 * @cfg {Boolean} loadMask (true|false) default false
7673 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675 * @cfg {Boolean} rowSelection (true|false) default false
7676 * @cfg {Boolean} cellSelection (true|false) default false
7677 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7679 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7680 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7684 * Create a new Table
7685 * @param {Object} config The config object
7688 Roo.bootstrap.Table = function(config){
7689 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7694 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7699 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7701 this.sm.grid = this;
7702 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703 this.sm = this.selModel;
7704 this.sm.xmodule = this.xmodule || false;
7707 if (this.cm && typeof(this.cm.config) == 'undefined') {
7708 this.colModel = new Roo.grid.ColumnModel(this.cm);
7709 this.cm = this.colModel;
7710 this.cm.xmodule = this.xmodule || false;
7713 this.store= Roo.factory(this.store, Roo.data);
7714 this.ds = this.store;
7715 this.ds.xmodule = this.xmodule || false;
7718 if (this.footer && this.store) {
7719 this.footer.dataSource = this.ds;
7720 this.footer = Roo.factory(this.footer);
7727 * Fires when a cell is clicked
7728 * @param {Roo.bootstrap.Table} this
7729 * @param {Roo.Element} el
7730 * @param {Number} rowIndex
7731 * @param {Number} columnIndex
7732 * @param {Roo.EventObject} e
7736 * @event celldblclick
7737 * Fires when a cell is double clicked
7738 * @param {Roo.bootstrap.Table} this
7739 * @param {Roo.Element} el
7740 * @param {Number} rowIndex
7741 * @param {Number} columnIndex
7742 * @param {Roo.EventObject} e
7744 "celldblclick" : true,
7747 * Fires when a row is clicked
7748 * @param {Roo.bootstrap.Table} this
7749 * @param {Roo.Element} el
7750 * @param {Number} rowIndex
7751 * @param {Roo.EventObject} e
7755 * @event rowdblclick
7756 * Fires when a row is double clicked
7757 * @param {Roo.bootstrap.Table} this
7758 * @param {Roo.Element} el
7759 * @param {Number} rowIndex
7760 * @param {Roo.EventObject} e
7762 "rowdblclick" : true,
7765 * Fires when a mouseover occur
7766 * @param {Roo.bootstrap.Table} this
7767 * @param {Roo.Element} el
7768 * @param {Number} rowIndex
7769 * @param {Number} columnIndex
7770 * @param {Roo.EventObject} e
7775 * Fires when a mouseout occur
7776 * @param {Roo.bootstrap.Table} this
7777 * @param {Roo.Element} el
7778 * @param {Number} rowIndex
7779 * @param {Number} columnIndex
7780 * @param {Roo.EventObject} e
7785 * Fires when a row is rendered, so you can change add a style to it.
7786 * @param {Roo.bootstrap.Table} this
7787 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7791 * @event rowsrendered
7792 * Fires when all the rows have been rendered
7793 * @param {Roo.bootstrap.Table} this
7795 'rowsrendered' : true,
7797 * @event contextmenu
7798 * The raw contextmenu event for the entire grid.
7799 * @param {Roo.EventObject} e
7801 "contextmenu" : true,
7803 * @event rowcontextmenu
7804 * Fires when a row is right clicked
7805 * @param {Roo.bootstrap.Table} this
7806 * @param {Number} rowIndex
7807 * @param {Roo.EventObject} e
7809 "rowcontextmenu" : true,
7811 * @event cellcontextmenu
7812 * Fires when a cell is right clicked
7813 * @param {Roo.bootstrap.Table} this
7814 * @param {Number} rowIndex
7815 * @param {Number} cellIndex
7816 * @param {Roo.EventObject} e
7818 "cellcontextmenu" : true,
7820 * @event headercontextmenu
7821 * Fires when a header is right clicked
7822 * @param {Roo.bootstrap.Table} this
7823 * @param {Number} columnIndex
7824 * @param {Roo.EventObject} e
7826 "headercontextmenu" : true
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7856 rowSelection : false,
7857 cellSelection : false,
7860 // Roo.Element - the tbody
7862 // Roo.Element - thead element
7865 container: false, // used by gridpanel...
7871 auto_hide_footer : false,
7873 getAutoCreate : function()
7875 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7882 if (this.scrollBody) {
7883 cfg.cls += ' table-body-fixed';
7886 cfg.cls += ' table-striped';
7890 cfg.cls += ' table-hover';
7892 if (this.bordered) {
7893 cfg.cls += ' table-bordered';
7895 if (this.condensed) {
7896 cfg.cls += ' table-condensed';
7898 if (this.responsive) {
7899 cfg.cls += ' table-responsive';
7903 cfg.cls+= ' ' +this.cls;
7906 // this lot should be simplifed...
7919 ].forEach(function(k) {
7927 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7930 if(this.store || this.cm){
7931 if(this.headerShow){
7932 cfg.cn.push(this.renderHeader());
7935 cfg.cn.push(this.renderBody());
7937 if(this.footerShow){
7938 cfg.cn.push(this.renderFooter());
7940 // where does this come from?
7941 //cfg.cls+= ' TableGrid';
7944 return { cn : [ cfg ] };
7947 initEvents : function()
7949 if(!this.store || !this.cm){
7952 if (this.selModel) {
7953 this.selModel.initEvents();
7957 //Roo.log('initEvents with ds!!!!');
7959 this.mainBody = this.el.select('tbody', true).first();
7960 this.mainHead = this.el.select('thead', true).first();
7961 this.mainFoot = this.el.select('tfoot', true).first();
7967 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968 e.on('click', _this.sort, _this);
7971 this.mainBody.on("click", this.onClick, this);
7972 this.mainBody.on("dblclick", this.onDblClick, this);
7974 // why is this done????? = it breaks dialogs??
7975 //this.parent().el.setStyle('position', 'relative');
7979 this.footer.parentId = this.id;
7980 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7983 this.el.select('tfoot tr td').first().addClass('hide');
7988 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7991 this.store.on('load', this.onLoad, this);
7992 this.store.on('beforeload', this.onBeforeLoad, this);
7993 this.store.on('update', this.onUpdate, this);
7994 this.store.on('add', this.onAdd, this);
7995 this.store.on("clear", this.clear, this);
7997 this.el.on("contextmenu", this.onContextMenu, this);
7999 this.mainBody.on('scroll', this.onBodyScroll, this);
8001 this.cm.on("headerchange", this.onHeaderChange, this);
8003 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8007 onContextMenu : function(e, t)
8009 this.processEvent("contextmenu", e);
8012 processEvent : function(name, e)
8014 if (name != 'touchstart' ) {
8015 this.fireEvent(name, e);
8018 var t = e.getTarget();
8020 var cell = Roo.get(t);
8026 if(cell.findParent('tfoot', false, true)){
8030 if(cell.findParent('thead', false, true)){
8032 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033 cell = Roo.get(t).findParent('th', false, true);
8035 Roo.log("failed to find th in thead?");
8036 Roo.log(e.getTarget());
8041 var cellIndex = cell.dom.cellIndex;
8043 var ename = name == 'touchstart' ? 'click' : name;
8044 this.fireEvent("header" + ename, this, cellIndex, e);
8049 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050 cell = Roo.get(t).findParent('td', false, true);
8052 Roo.log("failed to find th in tbody?");
8053 Roo.log(e.getTarget());
8058 var row = cell.findParent('tr', false, true);
8059 var cellIndex = cell.dom.cellIndex;
8060 var rowIndex = row.dom.rowIndex - 1;
8064 this.fireEvent("row" + name, this, rowIndex, e);
8068 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8074 onMouseover : function(e, el)
8076 var cell = Roo.get(el);
8082 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083 cell = cell.findParent('td', false, true);
8086 var row = cell.findParent('tr', false, true);
8087 var cellIndex = cell.dom.cellIndex;
8088 var rowIndex = row.dom.rowIndex - 1; // start from 0
8090 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8094 onMouseout : function(e, el)
8096 var cell = Roo.get(el);
8102 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103 cell = cell.findParent('td', false, true);
8106 var row = cell.findParent('tr', false, true);
8107 var cellIndex = cell.dom.cellIndex;
8108 var rowIndex = row.dom.rowIndex - 1; // start from 0
8110 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8114 onClick : function(e, el)
8116 var cell = Roo.get(el);
8118 if(!cell || (!this.cellSelection && !this.rowSelection)){
8122 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123 cell = cell.findParent('td', false, true);
8126 if(!cell || typeof(cell) == 'undefined'){
8130 var row = cell.findParent('tr', false, true);
8132 if(!row || typeof(row) == 'undefined'){
8136 var cellIndex = cell.dom.cellIndex;
8137 var rowIndex = this.getRowIndex(row);
8139 // why??? - should these not be based on SelectionModel?
8140 if(this.cellSelection){
8141 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8144 if(this.rowSelection){
8145 this.fireEvent('rowclick', this, row, rowIndex, e);
8151 onDblClick : function(e,el)
8153 var cell = Roo.get(el);
8155 if(!cell || (!this.cellSelection && !this.rowSelection)){
8159 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160 cell = cell.findParent('td', false, true);
8163 if(!cell || typeof(cell) == 'undefined'){
8167 var row = cell.findParent('tr', false, true);
8169 if(!row || typeof(row) == 'undefined'){
8173 var cellIndex = cell.dom.cellIndex;
8174 var rowIndex = this.getRowIndex(row);
8176 if(this.cellSelection){
8177 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8180 if(this.rowSelection){
8181 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8185 sort : function(e,el)
8187 var col = Roo.get(el);
8189 if(!col.hasClass('sortable')){
8193 var sort = col.attr('sort');
8196 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8200 this.store.sortInfo = {field : sort, direction : dir};
8203 Roo.log("calling footer first");
8204 this.footer.onClick('first');
8207 this.store.load({ params : { start : 0 } });
8211 renderHeader : function()
8219 this.totalWidth = 0;
8221 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8223 var config = cm.config[i];
8227 cls : 'x-hcol-' + i,
8229 html: cm.getColumnHeader(i)
8234 if(typeof(config.sortable) != 'undefined' && config.sortable){
8236 c.html = '<i class="glyphicon"></i>' + c.html;
8239 // could use BS4 hidden-..-down
8241 if(typeof(config.lgHeader) != 'undefined'){
8242 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8245 if(typeof(config.mdHeader) != 'undefined'){
8246 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8249 if(typeof(config.smHeader) != 'undefined'){
8250 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8253 if(typeof(config.xsHeader) != 'undefined'){
8254 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8261 if(typeof(config.tooltip) != 'undefined'){
8262 c.tooltip = config.tooltip;
8265 if(typeof(config.colspan) != 'undefined'){
8266 c.colspan = config.colspan;
8269 if(typeof(config.hidden) != 'undefined' && config.hidden){
8270 c.style += ' display:none;';
8273 if(typeof(config.dataIndex) != 'undefined'){
8274 c.sort = config.dataIndex;
8279 if(typeof(config.align) != 'undefined' && config.align.length){
8280 c.style += ' text-align:' + config.align + ';';
8283 if(typeof(config.width) != 'undefined'){
8284 c.style += ' width:' + config.width + 'px;';
8285 this.totalWidth += config.width;
8287 this.totalWidth += 100; // assume minimum of 100 per column?
8290 if(typeof(config.cls) != 'undefined'){
8291 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8294 ['xs','sm','md','lg'].map(function(size){
8296 if(typeof(config[size]) == 'undefined'){
8300 if (!config[size]) { // 0 = hidden
8301 // BS 4 '0' is treated as hide that column and below.
8302 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8306 c.cls += ' col-' + size + '-' + config[size] + (
8307 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8319 renderBody : function()
8329 colspan : this.cm.getColumnCount()
8339 renderFooter : function()
8349 colspan : this.cm.getColumnCount()
8363 // Roo.log('ds onload');
8368 var ds = this.store;
8370 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372 if (_this.store.sortInfo) {
8374 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375 e.select('i', true).addClass(['glyphicon-arrow-up']);
8378 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379 e.select('i', true).addClass(['glyphicon-arrow-down']);
8384 var tbody = this.mainBody;
8386 if(ds.getCount() > 0){
8387 ds.data.each(function(d,rowIndex){
8388 var row = this.renderRow(cm, ds, rowIndex);
8390 tbody.createChild(row);
8394 if(row.cellObjects.length){
8395 Roo.each(row.cellObjects, function(r){
8396 _this.renderCellObject(r);
8403 var tfoot = this.el.select('tfoot', true).first();
8405 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8407 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8409 var total = this.ds.getTotalCount();
8411 if(this.footer.pageSize < total){
8412 this.mainFoot.show();
8416 Roo.each(this.el.select('tbody td', true).elements, function(e){
8417 e.on('mouseover', _this.onMouseover, _this);
8420 Roo.each(this.el.select('tbody td', true).elements, function(e){
8421 e.on('mouseout', _this.onMouseout, _this);
8423 this.fireEvent('rowsrendered', this);
8429 onUpdate : function(ds,record)
8431 this.refreshRow(record);
8435 onRemove : function(ds, record, index, isUpdate){
8436 if(isUpdate !== true){
8437 this.fireEvent("beforerowremoved", this, index, record);
8439 var bt = this.mainBody.dom;
8441 var rows = this.el.select('tbody > tr', true).elements;
8443 if(typeof(rows[index]) != 'undefined'){
8444 bt.removeChild(rows[index].dom);
8447 // if(bt.rows[index]){
8448 // bt.removeChild(bt.rows[index]);
8451 if(isUpdate !== true){
8452 //this.stripeRows(index);
8453 //this.syncRowHeights(index, index);
8455 this.fireEvent("rowremoved", this, index, record);
8459 onAdd : function(ds, records, rowIndex)
8461 //Roo.log('on Add called');
8462 // - note this does not handle multiple adding very well..
8463 var bt = this.mainBody.dom;
8464 for (var i =0 ; i < records.length;i++) {
8465 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466 //Roo.log(records[i]);
8467 //Roo.log(this.store.getAt(rowIndex+i));
8468 this.insertRow(this.store, rowIndex + i, false);
8475 refreshRow : function(record){
8476 var ds = this.store, index;
8477 if(typeof record == 'number'){
8479 record = ds.getAt(index);
8481 index = ds.indexOf(record);
8483 return; // should not happen - but seems to
8486 this.insertRow(ds, index, true);
8488 this.onRemove(ds, record, index+1, true);
8490 //this.syncRowHeights(index, index);
8492 this.fireEvent("rowupdated", this, index, record);
8495 insertRow : function(dm, rowIndex, isUpdate){
8498 this.fireEvent("beforerowsinserted", this, rowIndex);
8500 //var s = this.getScrollState();
8501 var row = this.renderRow(this.cm, this.store, rowIndex);
8502 // insert before rowIndex..
8503 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8507 if(row.cellObjects.length){
8508 Roo.each(row.cellObjects, function(r){
8509 _this.renderCellObject(r);
8514 this.fireEvent("rowsinserted", this, rowIndex);
8515 //this.syncRowHeights(firstRow, lastRow);
8516 //this.stripeRows(firstRow);
8523 getRowDom : function(rowIndex)
8525 var rows = this.el.select('tbody > tr', true).elements;
8527 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8530 // returns the object tree for a tr..
8533 renderRow : function(cm, ds, rowIndex)
8535 var d = ds.getAt(rowIndex);
8539 cls : 'x-row-' + rowIndex,
8543 var cellObjects = [];
8545 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546 var config = cm.config[i];
8548 var renderer = cm.getRenderer(i);
8552 if(typeof(renderer) !== 'undefined'){
8553 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8555 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556 // and are rendered into the cells after the row is rendered - using the id for the element.
8558 if(typeof(value) === 'object'){
8568 rowIndex : rowIndex,
8573 this.fireEvent('rowclass', this, rowcfg);
8577 cls : rowcfg.rowClass + ' x-col-' + i,
8579 html: (typeof(value) === 'object') ? '' : value
8586 if(typeof(config.colspan) != 'undefined'){
8587 td.colspan = config.colspan;
8590 if(typeof(config.hidden) != 'undefined' && config.hidden){
8591 td.style += ' display:none;';
8594 if(typeof(config.align) != 'undefined' && config.align.length){
8595 td.style += ' text-align:' + config.align + ';';
8597 if(typeof(config.valign) != 'undefined' && config.valign.length){
8598 td.style += ' vertical-align:' + config.valign + ';';
8601 if(typeof(config.width) != 'undefined'){
8602 td.style += ' width:' + config.width + 'px;';
8605 if(typeof(config.cursor) != 'undefined'){
8606 td.style += ' cursor:' + config.cursor + ';';
8609 if(typeof(config.cls) != 'undefined'){
8610 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8613 ['xs','sm','md','lg'].map(function(size){
8615 if(typeof(config[size]) == 'undefined'){
8621 if (!config[size]) { // 0 = hidden
8622 // BS 4 '0' is treated as hide that column and below.
8623 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8627 td.cls += ' col-' + size + '-' + config[size] + (
8628 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8638 row.cellObjects = cellObjects;
8646 onBeforeLoad : function()
8655 this.el.select('tbody', true).first().dom.innerHTML = '';
8658 * Show or hide a row.
8659 * @param {Number} rowIndex to show or hide
8660 * @param {Boolean} state hide
8662 setRowVisibility : function(rowIndex, state)
8664 var bt = this.mainBody.dom;
8666 var rows = this.el.select('tbody > tr', true).elements;
8668 if(typeof(rows[rowIndex]) == 'undefined'){
8671 rows[rowIndex].dom.style.display = state ? '' : 'none';
8675 getSelectionModel : function(){
8677 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8679 return this.selModel;
8682 * Render the Roo.bootstrap object from renderder
8684 renderCellObject : function(r)
8688 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8690 var t = r.cfg.render(r.container);
8693 Roo.each(r.cfg.cn, function(c){
8695 container: t.getChildContainer(),
8698 _this.renderCellObject(child);
8703 getRowIndex : function(row)
8707 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8718 * Returns the grid's underlying element = used by panel.Grid
8719 * @return {Element} The element
8721 getGridEl : function(){
8725 * Forces a resize - used by panel.Grid
8726 * @return {Element} The element
8728 autoSize : function()
8730 //var ctr = Roo.get(this.container.dom.parentElement);
8731 var ctr = Roo.get(this.el.dom);
8733 var thd = this.getGridEl().select('thead',true).first();
8734 var tbd = this.getGridEl().select('tbody', true).first();
8735 var tfd = this.getGridEl().select('tfoot', true).first();
8737 var cw = ctr.getWidth();
8738 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8742 tbd.setWidth(ctr.getWidth());
8743 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744 // this needs fixing for various usage - currently only hydra job advers I think..
8746 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8748 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8751 cw = Math.max(cw, this.totalWidth);
8752 this.getGridEl().select('tbody tr',true).setWidth(cw);
8754 // resize 'expandable coloumn?
8756 return; // we doe not have a view in this design..
8759 onBodyScroll: function()
8761 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8763 this.mainHead.setStyle({
8764 'position' : 'relative',
8765 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8771 var scrollHeight = this.mainBody.dom.scrollHeight;
8773 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8775 var height = this.mainBody.getHeight();
8777 if(scrollHeight - height == scrollTop) {
8779 var total = this.ds.getTotalCount();
8781 if(this.footer.cursor + this.footer.pageSize < total){
8783 this.footer.ds.load({
8785 start : this.footer.cursor + this.footer.pageSize,
8786 limit : this.footer.pageSize
8796 onHeaderChange : function()
8798 var header = this.renderHeader();
8799 var table = this.el.select('table', true).first();
8801 this.mainHead.remove();
8802 this.mainHead = table.createChild(header, this.mainBody, false);
8805 onHiddenChange : function(colModel, colIndex, hidden)
8807 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8810 this.CSS.updateRule(thSelector, "display", "");
8811 this.CSS.updateRule(tdSelector, "display", "");
8814 this.CSS.updateRule(thSelector, "display", "none");
8815 this.CSS.updateRule(tdSelector, "display", "none");
8818 this.onHeaderChange();
8822 setColumnWidth: function(col_index, width)
8824 // width = "md-2 xs-2..."
8825 if(!this.colModel.config[col_index]) {
8829 var w = width.split(" ");
8831 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8833 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8836 for(var j = 0; j < w.length; j++) {
8842 var size_cls = w[j].split("-");
8844 if(!Number.isInteger(size_cls[1] * 1)) {
8848 if(!this.colModel.config[col_index][size_cls[0]]) {
8852 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8856 h_row[0].classList.replace(
8857 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858 "col-"+size_cls[0]+"-"+size_cls[1]
8861 for(var i = 0; i < rows.length; i++) {
8863 var size_cls = w[j].split("-");
8865 if(!Number.isInteger(size_cls[1] * 1)) {
8869 if(!this.colModel.config[col_index][size_cls[0]]) {
8873 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8877 rows[i].classList.replace(
8878 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879 "col-"+size_cls[0]+"-"+size_cls[1]
8883 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8898 * @class Roo.bootstrap.TableCell
8899 * @extends Roo.bootstrap.Component
8900 * Bootstrap TableCell class
8901 * @cfg {String} html cell contain text
8902 * @cfg {String} cls cell class
8903 * @cfg {String} tag cell tag (td|th) default td
8904 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905 * @cfg {String} align Aligns the content in a cell
8906 * @cfg {String} axis Categorizes cells
8907 * @cfg {String} bgcolor Specifies the background color of a cell
8908 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909 * @cfg {Number} colspan Specifies the number of columns a cell should span
8910 * @cfg {String} headers Specifies one or more header cells a cell is related to
8911 * @cfg {Number} height Sets the height of a cell
8912 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913 * @cfg {Number} rowspan Sets the number of rows a cell should span
8914 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915 * @cfg {String} valign Vertical aligns the content in a cell
8916 * @cfg {Number} width Specifies the width of a cell
8919 * Create a new TableCell
8920 * @param {Object} config The config object
8923 Roo.bootstrap.TableCell = function(config){
8924 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8947 getAutoCreate : function(){
8948 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8968 cfg.align=this.align
8974 cfg.bgcolor=this.bgcolor
8977 cfg.charoff=this.charoff
8980 cfg.colspan=this.colspan
8983 cfg.headers=this.headers
8986 cfg.height=this.height
8989 cfg.nowrap=this.nowrap
8992 cfg.rowspan=this.rowspan
8995 cfg.scope=this.scope
8998 cfg.valign=this.valign
9001 cfg.width=this.width
9020 * @class Roo.bootstrap.TableRow
9021 * @extends Roo.bootstrap.Component
9022 * Bootstrap TableRow class
9023 * @cfg {String} cls row class
9024 * @cfg {String} align Aligns the content in a table row
9025 * @cfg {String} bgcolor Specifies a background color for a table row
9026 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027 * @cfg {String} valign Vertical aligns the content in a table row
9030 * Create a new TableRow
9031 * @param {Object} config The config object
9034 Roo.bootstrap.TableRow = function(config){
9035 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9046 getAutoCreate : function(){
9047 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9057 cfg.align = this.align;
9060 cfg.bgcolor = this.bgcolor;
9063 cfg.charoff = this.charoff;
9066 cfg.valign = this.valign;
9084 * @class Roo.bootstrap.TableBody
9085 * @extends Roo.bootstrap.Component
9086 * Bootstrap TableBody class
9087 * @cfg {String} cls element class
9088 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089 * @cfg {String} align Aligns the content inside the element
9090 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9094 * Create a new TableBody
9095 * @param {Object} config The config object
9098 Roo.bootstrap.TableBody = function(config){
9099 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9110 getAutoCreate : function(){
9111 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9125 cfg.align = this.align;
9128 cfg.charoff = this.charoff;
9131 cfg.valign = this.valign;
9138 // initEvents : function()
9145 // this.store = Roo.factory(this.store, Roo.data);
9146 // this.store.on('load', this.onLoad, this);
9148 // this.store.load();
9152 // onLoad: function ()
9154 // this.fireEvent('load', this);
9164 * Ext JS Library 1.1.1
9165 * Copyright(c) 2006-2007, Ext JS, LLC.
9167 * Originally Released Under LGPL - original licence link has changed is not relivant.
9170 * <script type="text/javascript">
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9176 * @class Roo.form.Action
9177 * Internal Class used to handle form actions
9179 * @param {Roo.form.BasicForm} el The form element or its id
9180 * @param {Object} config Configuration options
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9188 this.options = options || {};
9191 * Client Validation Failed
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9196 * Server Validation Failed
9199 Roo.form.Action.SERVER_INVALID = 'server';
9201 * Connect to Server Failed
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9206 * Reading Data from Server Failed
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9211 Roo.form.Action.prototype = {
9213 failureType : undefined,
9214 response : undefined,
9218 run : function(options){
9223 success : function(response){
9228 handleResponse : function(response){
9232 // default connection failure
9233 failure : function(response){
9235 this.response = response;
9236 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237 this.form.afterAction(this, false);
9240 processResponse : function(response){
9241 this.response = response;
9242 if(!response.responseText){
9245 this.result = this.handleResponse(response);
9249 // utility functions used internally
9250 getUrl : function(appendParams){
9251 var url = this.options.url || this.form.url || this.form.el.dom.action;
9253 var p = this.getParams();
9255 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9261 getMethod : function(){
9262 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9265 getParams : function(){
9266 var bp = this.form.baseParams;
9267 var p = this.options.params;
9269 if(typeof p == "object"){
9270 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271 }else if(typeof p == 'string' && bp){
9272 p += '&' + Roo.urlEncode(bp);
9275 p = Roo.urlEncode(bp);
9280 createCallback : function(){
9282 success: this.success,
9283 failure: this.failure,
9285 timeout: (this.form.timeout*1000),
9286 upload: this.form.fileUpload ? this.success : undefined
9291 Roo.form.Action.Submit = function(form, options){
9292 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9298 haveProgress : false,
9299 uploadComplete : false,
9301 // uploadProgress indicator.
9302 uploadProgress : function()
9304 if (!this.form.progressUrl) {
9308 if (!this.haveProgress) {
9309 Roo.MessageBox.progress("Uploading", "Uploading");
9311 if (this.uploadComplete) {
9312 Roo.MessageBox.hide();
9316 this.haveProgress = true;
9318 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9320 var c = new Roo.data.Connection();
9322 url : this.form.progressUrl,
9327 success : function(req){
9328 //console.log(data);
9332 rdata = Roo.decode(req.responseText)
9334 Roo.log("Invalid data from server..");
9338 if (!rdata || !rdata.success) {
9340 Roo.MessageBox.alert(Roo.encode(rdata));
9343 var data = rdata.data;
9345 if (this.uploadComplete) {
9346 Roo.MessageBox.hide();
9351 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9355 this.uploadProgress.defer(2000,this);
9358 failure: function(data) {
9359 Roo.log('progress url failed ');
9370 // run get Values on the form, so it syncs any secondary forms.
9371 this.form.getValues();
9373 var o = this.options;
9374 var method = this.getMethod();
9375 var isPost = method == 'POST';
9376 if(o.clientValidation === false || this.form.isValid()){
9378 if (this.form.progressUrl) {
9379 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380 (new Date() * 1) + '' + Math.random());
9385 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386 form:this.form.el.dom,
9387 url:this.getUrl(!isPost),
9389 params:isPost ? this.getParams() : null,
9390 isUpload: this.form.fileUpload,
9391 formData : this.form.formData
9394 this.uploadProgress();
9396 }else if (o.clientValidation !== false){ // client validation failed
9397 this.failureType = Roo.form.Action.CLIENT_INVALID;
9398 this.form.afterAction(this, false);
9402 success : function(response)
9404 this.uploadComplete= true;
9405 if (this.haveProgress) {
9406 Roo.MessageBox.hide();
9410 var result = this.processResponse(response);
9411 if(result === true || result.success){
9412 this.form.afterAction(this, true);
9416 this.form.markInvalid(result.errors);
9417 this.failureType = Roo.form.Action.SERVER_INVALID;
9419 this.form.afterAction(this, false);
9421 failure : function(response)
9423 this.uploadComplete= true;
9424 if (this.haveProgress) {
9425 Roo.MessageBox.hide();
9428 this.response = response;
9429 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430 this.form.afterAction(this, false);
9433 handleResponse : function(response){
9434 if(this.form.errorReader){
9435 var rs = this.form.errorReader.read(response);
9438 for(var i = 0, len = rs.records.length; i < len; i++) {
9439 var r = rs.records[i];
9443 if(errors.length < 1){
9447 success : rs.success,
9453 ret = Roo.decode(response.responseText);
9457 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9467 Roo.form.Action.Load = function(form, options){
9468 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469 this.reader = this.form.reader;
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9477 Roo.Ajax.request(Roo.apply(
9478 this.createCallback(), {
9479 method:this.getMethod(),
9480 url:this.getUrl(false),
9481 params:this.getParams()
9485 success : function(response){
9487 var result = this.processResponse(response);
9488 if(result === true || !result.success || !result.data){
9489 this.failureType = Roo.form.Action.LOAD_FAILURE;
9490 this.form.afterAction(this, false);
9493 this.form.clearInvalid();
9494 this.form.setValues(result.data);
9495 this.form.afterAction(this, true);
9498 handleResponse : function(response){
9499 if(this.form.reader){
9500 var rs = this.form.reader.read(response);
9501 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9503 success : rs.success,
9507 return Roo.decode(response.responseText);
9511 Roo.form.Action.ACTION_TYPES = {
9512 'load' : Roo.form.Action.Load,
9513 'submit' : Roo.form.Action.Submit
9522 * @class Roo.bootstrap.Form
9523 * @extends Roo.bootstrap.Component
9524 * Bootstrap Form class
9525 * @cfg {String} method GET | POST (default POST)
9526 * @cfg {String} labelAlign top | left (default top)
9527 * @cfg {String} align left | right - for navbars
9528 * @cfg {Boolean} loadMask load mask when submit (default true)
9533 * @param {Object} config The config object
9537 Roo.bootstrap.Form = function(config){
9539 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9541 Roo.bootstrap.Form.popover.apply();
9545 * @event clientvalidation
9546 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547 * @param {Form} this
9548 * @param {Boolean} valid true if the form has passed client-side validation
9550 clientvalidation: true,
9552 * @event beforeaction
9553 * Fires before any action is performed. Return false to cancel the action.
9554 * @param {Form} this
9555 * @param {Action} action The action to be performed
9559 * @event actionfailed
9560 * Fires when an action fails.
9561 * @param {Form} this
9562 * @param {Action} action The action that failed
9564 actionfailed : true,
9566 * @event actioncomplete
9567 * Fires when an action is completed.
9568 * @param {Form} this
9569 * @param {Action} action The action that completed
9571 actioncomplete : true
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9578 * @cfg {String} method
9579 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9584 * The URL to use for form actions if one isn't supplied in the action options.
9587 * @cfg {Boolean} fileUpload
9588 * Set to true if this form is a file upload.
9592 * @cfg {Object} baseParams
9593 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9597 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9601 * @cfg {Sting} align (left|right) for navbar forms
9606 activeAction : null,
9609 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610 * element by passing it or its id or mask the form itself by passing in true.
9613 waitMsgTarget : false,
9618 * @cfg {Boolean} errorMask (true|false) default false
9623 * @cfg {Number} maskOffset Default 100
9628 * @cfg {Boolean} maskBody
9632 getAutoCreate : function(){
9636 method : this.method || 'POST',
9637 id : this.id || Roo.id(),
9640 if (this.parent().xtype.match(/^Nav/)) {
9641 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9645 if (this.labelAlign == 'left' ) {
9646 cfg.cls += ' form-horizontal';
9652 initEvents : function()
9654 this.el.on('submit', this.onSubmit, this);
9655 // this was added as random key presses on the form where triggering form submit.
9656 this.el.on('keypress', function(e) {
9657 if (e.getCharCode() != 13) {
9660 // we might need to allow it for textareas.. and some other items.
9661 // check e.getTarget().
9663 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9667 Roo.log("keypress blocked");
9675 onSubmit : function(e){
9680 * Returns true if client-side validation on the form is successful.
9683 isValid : function(){
9684 var items = this.getItems();
9688 items.each(function(f){
9694 Roo.log('invalid field: ' + f.name);
9698 if(!target && f.el.isVisible(true)){
9704 if(this.errorMask && !valid){
9705 Roo.bootstrap.Form.popover.mask(this, target);
9712 * Returns true if any fields in this form have changed since their original load.
9715 isDirty : function(){
9717 var items = this.getItems();
9718 items.each(function(f){
9728 * Performs a predefined action (submit or load) or custom actions you define on this form.
9729 * @param {String} actionName The name of the action type
9730 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9731 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732 * accept other config options):
9734 Property Type Description
9735 ---------------- --------------- ----------------------------------------------------------------------------------
9736 url String The url for the action (defaults to the form's url)
9737 method String The form method to use (defaults to the form's method, or POST if not defined)
9738 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9740 validate the form on the client (defaults to false)
9742 * @return {BasicForm} this
9744 doAction : function(action, options){
9745 if(typeof action == 'string'){
9746 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9748 if(this.fireEvent('beforeaction', this, action) !== false){
9749 this.beforeAction(action);
9750 action.run.defer(100, action);
9756 beforeAction : function(action){
9757 var o = action.options;
9762 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9764 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9767 // not really supported yet.. ??
9769 //if(this.waitMsgTarget === true){
9770 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771 //}else if(this.waitMsgTarget){
9772 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9775 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9781 afterAction : function(action, success){
9782 this.activeAction = null;
9783 var o = action.options;
9788 Roo.get(document.body).unmask();
9794 //if(this.waitMsgTarget === true){
9795 // this.el.unmask();
9796 //}else if(this.waitMsgTarget){
9797 // this.waitMsgTarget.unmask();
9799 // Roo.MessageBox.updateProgress(1);
9800 // Roo.MessageBox.hide();
9807 Roo.callback(o.success, o.scope, [this, action]);
9808 this.fireEvent('actioncomplete', this, action);
9812 // failure condition..
9813 // we have a scenario where updates need confirming.
9814 // eg. if a locking scenario exists..
9815 // we look for { errors : { needs_confirm : true }} in the response.
9817 (typeof(action.result) != 'undefined') &&
9818 (typeof(action.result.errors) != 'undefined') &&
9819 (typeof(action.result.errors.needs_confirm) != 'undefined')
9822 Roo.log("not supported yet");
9825 Roo.MessageBox.confirm(
9826 "Change requires confirmation",
9827 action.result.errorMsg,
9832 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9842 Roo.callback(o.failure, o.scope, [this, action]);
9843 // show an error message if no failed handler is set..
9844 if (!this.hasListener('actionfailed')) {
9845 Roo.log("need to add dialog support");
9847 Roo.MessageBox.alert("Error",
9848 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849 action.result.errorMsg :
9850 "Saving Failed, please check your entries or try again"
9855 this.fireEvent('actionfailed', this, action);
9860 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861 * @param {String} id The value to search for
9864 findField : function(id){
9865 var items = this.getItems();
9866 var field = items.get(id);
9868 items.each(function(f){
9869 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9876 return field || null;
9879 * Mark fields in this form invalid in bulk.
9880 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881 * @return {BasicForm} this
9883 markInvalid : function(errors){
9884 if(errors instanceof Array){
9885 for(var i = 0, len = errors.length; i < len; i++){
9886 var fieldError = errors[i];
9887 var f = this.findField(fieldError.id);
9889 f.markInvalid(fieldError.msg);
9895 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896 field.markInvalid(errors[id]);
9900 //Roo.each(this.childForms || [], function (f) {
9901 // f.markInvalid(errors);
9908 * Set values for fields in this form in bulk.
9909 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910 * @return {BasicForm} this
9912 setValues : function(values){
9913 if(values instanceof Array){ // array of objects
9914 for(var i = 0, len = values.length; i < len; i++){
9916 var f = this.findField(v.id);
9918 f.setValue(v.value);
9919 if(this.trackResetOnLoad){
9920 f.originalValue = f.getValue();
9924 }else{ // object hash
9927 if(typeof values[id] != 'function' && (field = this.findField(id))){
9929 if (field.setFromData &&
9931 field.displayField &&
9932 // combos' with local stores can
9933 // be queried via setValue()
9934 // to set their value..
9935 (field.store && !field.store.isLocal)
9939 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941 field.setFromData(sd);
9943 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9945 field.setFromData(values);
9948 field.setValue(values[id]);
9952 if(this.trackResetOnLoad){
9953 field.originalValue = field.getValue();
9959 //Roo.each(this.childForms || [], function (f) {
9960 // f.setValues(values);
9967 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968 * they are returned as an array.
9969 * @param {Boolean} asString
9972 getValues : function(asString){
9973 //if (this.childForms) {
9974 // copy values from the child forms
9975 // Roo.each(this.childForms, function (f) {
9976 // this.setValues(f.getValues());
9982 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983 if(asString === true){
9986 return Roo.urlDecode(fs);
9990 * Returns the fields in this form as an object with key/value pairs.
9991 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9994 getFieldValues : function(with_hidden)
9996 var items = this.getItems();
9998 items.each(function(f){
10000 if (!f.getName()) {
10004 var v = f.getValue();
10006 if (f.inputType =='radio') {
10007 if (typeof(ret[f.getName()]) == 'undefined') {
10008 ret[f.getName()] = ''; // empty..
10011 if (!f.el.dom.checked) {
10015 v = f.el.dom.value;
10019 if(f.xtype == 'MoneyField'){
10020 ret[f.currencyName] = f.getCurrency();
10023 // not sure if this supported any more..
10024 if ((typeof(v) == 'object') && f.getRawValue) {
10025 v = f.getRawValue() ; // dates..
10027 // combo boxes where name != hiddenName...
10028 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029 ret[f.name] = f.getRawValue();
10031 ret[f.getName()] = v;
10038 * Clears all invalid messages in this form.
10039 * @return {BasicForm} this
10041 clearInvalid : function(){
10042 var items = this.getItems();
10044 items.each(function(f){
10052 * Resets this form.
10053 * @return {BasicForm} this
10055 reset : function(){
10056 var items = this.getItems();
10057 items.each(function(f){
10061 Roo.each(this.childForms || [], function (f) {
10069 getItems : function()
10071 var r=new Roo.util.MixedCollection(false, function(o){
10072 return o.id || (o.id = Roo.id());
10074 var iter = function(el) {
10081 Roo.each(el.items,function(e) {
10090 hideFields : function(items)
10092 Roo.each(items, function(i){
10094 var f = this.findField(i);
10105 showFields : function(items)
10107 Roo.each(items, function(i){
10109 var f = this.findField(i);
10122 Roo.apply(Roo.bootstrap.Form, {
10138 intervalID : false,
10144 if(this.isApplied){
10149 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10155 this.maskEl.top.enableDisplayMode("block");
10156 this.maskEl.left.enableDisplayMode("block");
10157 this.maskEl.bottom.enableDisplayMode("block");
10158 this.maskEl.right.enableDisplayMode("block");
10160 this.toolTip = new Roo.bootstrap.Tooltip({
10161 cls : 'roo-form-error-popover',
10163 'left' : ['r-l', [-2,0], 'right'],
10164 'right' : ['l-r', [2,0], 'left'],
10165 'bottom' : ['tl-bl', [0,2], 'top'],
10166 'top' : [ 'bl-tl', [0,-2], 'bottom']
10170 this.toolTip.render(Roo.get(document.body));
10172 this.toolTip.el.enableDisplayMode("block");
10174 Roo.get(document.body).on('click', function(){
10178 Roo.get(document.body).on('touchstart', function(){
10182 this.isApplied = true
10185 mask : function(form, target)
10189 this.target = target;
10191 if(!this.form.errorMask || !target.el){
10195 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10197 Roo.log(scrollable);
10199 var ot = this.target.el.calcOffsetsTo(scrollable);
10201 var scrollTo = ot[1] - this.form.maskOffset;
10203 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10205 scrollable.scrollTo('top', scrollTo);
10207 var box = this.target.el.getBox();
10209 var zIndex = Roo.bootstrap.Modal.zIndex++;
10212 this.maskEl.top.setStyle('position', 'absolute');
10213 this.maskEl.top.setStyle('z-index', zIndex);
10214 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215 this.maskEl.top.setLeft(0);
10216 this.maskEl.top.setTop(0);
10217 this.maskEl.top.show();
10219 this.maskEl.left.setStyle('position', 'absolute');
10220 this.maskEl.left.setStyle('z-index', zIndex);
10221 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222 this.maskEl.left.setLeft(0);
10223 this.maskEl.left.setTop(box.y - this.padding);
10224 this.maskEl.left.show();
10226 this.maskEl.bottom.setStyle('position', 'absolute');
10227 this.maskEl.bottom.setStyle('z-index', zIndex);
10228 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229 this.maskEl.bottom.setLeft(0);
10230 this.maskEl.bottom.setTop(box.bottom + this.padding);
10231 this.maskEl.bottom.show();
10233 this.maskEl.right.setStyle('position', 'absolute');
10234 this.maskEl.right.setStyle('z-index', zIndex);
10235 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236 this.maskEl.right.setLeft(box.right + this.padding);
10237 this.maskEl.right.setTop(box.y - this.padding);
10238 this.maskEl.right.show();
10240 this.toolTip.bindEl = this.target.el;
10242 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10244 var tip = this.target.blankText;
10246 if(this.target.getValue() !== '' ) {
10248 if (this.target.invalidText.length) {
10249 tip = this.target.invalidText;
10250 } else if (this.target.regexText.length){
10251 tip = this.target.regexText;
10255 this.toolTip.show(tip);
10257 this.intervalID = window.setInterval(function() {
10258 Roo.bootstrap.Form.popover.unmask();
10261 window.onwheel = function(){ return false;};
10263 (function(){ this.isMasked = true; }).defer(500, this);
10267 unmask : function()
10269 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10273 this.maskEl.top.setStyle('position', 'absolute');
10274 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275 this.maskEl.top.hide();
10277 this.maskEl.left.setStyle('position', 'absolute');
10278 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279 this.maskEl.left.hide();
10281 this.maskEl.bottom.setStyle('position', 'absolute');
10282 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283 this.maskEl.bottom.hide();
10285 this.maskEl.right.setStyle('position', 'absolute');
10286 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287 this.maskEl.right.hide();
10289 this.toolTip.hide();
10291 this.toolTip.el.hide();
10293 window.onwheel = function(){ return true;};
10295 if(this.intervalID){
10296 window.clearInterval(this.intervalID);
10297 this.intervalID = false;
10300 this.isMasked = false;
10310 * Ext JS Library 1.1.1
10311 * Copyright(c) 2006-2007, Ext JS, LLC.
10313 * Originally Released Under LGPL - original licence link has changed is not relivant.
10316 * <script type="text/javascript">
10319 * @class Roo.form.VTypes
10320 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10323 Roo.form.VTypes = function(){
10324 // closure these in so they are only created once.
10325 var alpha = /^[a-zA-Z_]+$/;
10326 var alphanum = /^[a-zA-Z0-9_]+$/;
10327 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10330 // All these messages and functions are configurable
10333 * The function used to validate email addresses
10334 * @param {String} value The email address
10336 'email' : function(v){
10337 return email.test(v);
10340 * The error text to display when the email validation function returns false
10343 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10345 * The keystroke filter mask to be applied on email input
10348 'emailMask' : /[a-z0-9_\.\-@]/i,
10351 * The function used to validate URLs
10352 * @param {String} value The URL
10354 'url' : function(v){
10355 return url.test(v);
10358 * The error text to display when the url validation function returns false
10361 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10364 * The function used to validate alpha values
10365 * @param {String} value The value
10367 'alpha' : function(v){
10368 return alpha.test(v);
10371 * The error text to display when the alpha validation function returns false
10374 'alphaText' : 'This field should only contain letters and _',
10376 * The keystroke filter mask to be applied on alpha input
10379 'alphaMask' : /[a-z_]/i,
10382 * The function used to validate alphanumeric values
10383 * @param {String} value The value
10385 'alphanum' : function(v){
10386 return alphanum.test(v);
10389 * The error text to display when the alphanumeric validation function returns false
10392 'alphanumText' : 'This field should only contain letters, numbers and _',
10394 * The keystroke filter mask to be applied on alphanumeric input
10397 'alphanumMask' : /[a-z0-9_]/i
10407 * @class Roo.bootstrap.Input
10408 * @extends Roo.bootstrap.Component
10409 * Bootstrap Input class
10410 * @cfg {Boolean} disabled is it disabled
10411 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10412 * @cfg {String} name name of the input
10413 * @cfg {string} fieldLabel - the label associated
10414 * @cfg {string} placeholder - placeholder to put in text.
10415 * @cfg {string} before - input group add on before
10416 * @cfg {string} after - input group add on after
10417 * @cfg {string} size - (lg|sm) or leave empty..
10418 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420 * @cfg {Number} md colspan out of 12 for computer-sized screens
10421 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422 * @cfg {string} value default value of the input
10423 * @cfg {Number} labelWidth set the width of label
10424 * @cfg {Number} labellg set the width of label (1-12)
10425 * @cfg {Number} labelmd set the width of label (1-12)
10426 * @cfg {Number} labelsm set the width of label (1-12)
10427 * @cfg {Number} labelxs set the width of label (1-12)
10428 * @cfg {String} labelAlign (top|left)
10429 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431 * @cfg {String} indicatorpos (left|right) default left
10432 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10436 * @cfg {String} align (left|center|right) Default left
10437 * @cfg {Boolean} forceFeedback (true|false) Default false
10440 * Create a new Input
10441 * @param {Object} config The config object
10444 Roo.bootstrap.Input = function(config){
10446 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10451 * Fires when this field receives input focus.
10452 * @param {Roo.form.Field} this
10457 * Fires when this field loses input focus.
10458 * @param {Roo.form.Field} this
10462 * @event specialkey
10463 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10464 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465 * @param {Roo.form.Field} this
10466 * @param {Roo.EventObject} e The event object
10471 * Fires just before the field blurs if the field value has changed.
10472 * @param {Roo.form.Field} this
10473 * @param {Mixed} newValue The new value
10474 * @param {Mixed} oldValue The original value
10479 * Fires after the field has been marked as invalid.
10480 * @param {Roo.form.Field} this
10481 * @param {String} msg The validation message
10486 * Fires after the field has been validated with no errors.
10487 * @param {Roo.form.Field} this
10492 * Fires after the key up
10493 * @param {Roo.form.Field} this
10494 * @param {Roo.EventObject} e The event Object
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10502 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503 automatic validation (defaults to "keyup").
10505 validationEvent : "keyup",
10507 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10509 validateOnBlur : true,
10511 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10513 validationDelay : 250,
10515 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10517 focusClass : "x-form-focus", // not needed???
10521 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10523 invalidClass : "has-warning",
10526 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10528 validClass : "has-success",
10531 * @cfg {Boolean} hasFeedback (true|false) default true
10533 hasFeedback : true,
10536 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10538 invalidFeedbackClass : "glyphicon-warning-sign",
10541 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10543 validFeedbackClass : "glyphicon-ok",
10546 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10548 selectOnFocus : false,
10551 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10555 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10560 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10562 disableKeyFilter : false,
10565 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10569 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10573 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10575 blankText : "Please complete this mandatory field",
10578 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10582 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10584 maxLength : Number.MAX_VALUE,
10586 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10588 minLengthText : "The minimum length for this field is {0}",
10590 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10592 maxLengthText : "The maximum length for this field is {0}",
10596 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597 * If available, this function will be called only after the basic validators all return true, and will be passed the
10598 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10602 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10608 * @cfg {String} regexText -- Depricated - use Invalid Text
10613 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10619 autocomplete: false,
10623 inputType : 'text',
10626 placeholder: false,
10631 preventMark: false,
10632 isFormField : true,
10635 labelAlign : false,
10638 formatedValue : false,
10639 forceFeedback : false,
10641 indicatorpos : 'left',
10651 parentLabelAlign : function()
10654 while (parent.parent()) {
10655 parent = parent.parent();
10656 if (typeof(parent.labelAlign) !='undefined') {
10657 return parent.labelAlign;
10664 getAutoCreate : function()
10666 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10672 if(this.inputType != 'hidden'){
10673 cfg.cls = 'form-group' //input-group
10679 type : this.inputType,
10680 value : this.value,
10681 cls : 'form-control',
10682 placeholder : this.placeholder || '',
10683 autocomplete : this.autocomplete || 'new-password'
10685 if (this.inputType == 'file') {
10686 input.style = 'overflow:hidden'; // why not in CSS?
10689 if(this.capture.length){
10690 input.capture = this.capture;
10693 if(this.accept.length){
10694 input.accept = this.accept + "/*";
10698 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10701 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702 input.maxLength = this.maxLength;
10705 if (this.disabled) {
10706 input.disabled=true;
10709 if (this.readOnly) {
10710 input.readonly=true;
10714 input.name = this.name;
10718 input.cls += ' input-' + this.size;
10722 ['xs','sm','md','lg'].map(function(size){
10723 if (settings[size]) {
10724 cfg.cls += ' col-' + size + '-' + settings[size];
10728 var inputblock = input;
10732 cls: 'glyphicon form-control-feedback'
10735 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10738 cls : 'has-feedback',
10746 if (this.before || this.after) {
10749 cls : 'input-group',
10753 if (this.before && typeof(this.before) == 'string') {
10755 inputblock.cn.push({
10757 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10761 if (this.before && typeof(this.before) == 'object') {
10762 this.before = Roo.factory(this.before);
10764 inputblock.cn.push({
10766 cls : 'roo-input-before input-group-prepend input-group-' +
10767 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10771 inputblock.cn.push(input);
10773 if (this.after && typeof(this.after) == 'string') {
10774 inputblock.cn.push({
10776 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10780 if (this.after && typeof(this.after) == 'object') {
10781 this.after = Roo.factory(this.after);
10783 inputblock.cn.push({
10785 cls : 'roo-input-after input-group-append input-group-' +
10786 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10790 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791 inputblock.cls += ' has-feedback';
10792 inputblock.cn.push(feedback);
10797 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798 tooltip : 'This field is required'
10800 if (this.allowBlank ) {
10801 indicator.style = this.allowBlank ? ' display:none' : '';
10803 if (align ==='left' && this.fieldLabel.length) {
10805 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10812 cls : 'control-label col-form-label',
10813 html : this.fieldLabel
10824 var labelCfg = cfg.cn[1];
10825 var contentCfg = cfg.cn[2];
10827 if(this.indicatorpos == 'right'){
10832 cls : 'control-label col-form-label',
10836 html : this.fieldLabel
10850 labelCfg = cfg.cn[0];
10851 contentCfg = cfg.cn[1];
10855 if(this.labelWidth > 12){
10856 labelCfg.style = "width: " + this.labelWidth + 'px';
10859 if(this.labelWidth < 13 && this.labelmd == 0){
10860 this.labelmd = this.labelWidth;
10863 if(this.labellg > 0){
10864 labelCfg.cls += ' col-lg-' + this.labellg;
10865 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10868 if(this.labelmd > 0){
10869 labelCfg.cls += ' col-md-' + this.labelmd;
10870 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10873 if(this.labelsm > 0){
10874 labelCfg.cls += ' col-sm-' + this.labelsm;
10875 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10878 if(this.labelxs > 0){
10879 labelCfg.cls += ' col-xs-' + this.labelxs;
10880 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10884 } else if ( this.fieldLabel.length) {
10891 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892 tooltip : 'This field is required',
10893 style : this.allowBlank ? ' display:none' : ''
10897 //cls : 'input-group-addon',
10898 html : this.fieldLabel
10906 if(this.indicatorpos == 'right'){
10911 //cls : 'input-group-addon',
10912 html : this.fieldLabel
10917 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918 tooltip : 'This field is required',
10919 style : this.allowBlank ? ' display:none' : ''
10939 if (this.parentType === 'Navbar' && this.parent().bar) {
10940 cfg.cls += ' navbar-form';
10943 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944 // on BS4 we do this only if not form
10945 cfg.cls += ' navbar-form';
10953 * return the real input element.
10955 inputEl: function ()
10957 return this.el.select('input.form-control',true).first();
10960 tooltipEl : function()
10962 return this.inputEl();
10965 indicatorEl : function()
10967 if (Roo.bootstrap.version == 4) {
10968 return false; // not enabled in v4 yet.
10971 var indicator = this.el.select('i.roo-required-indicator',true).first();
10981 setDisabled : function(v)
10983 var i = this.inputEl().dom;
10985 i.removeAttribute('disabled');
10989 i.setAttribute('disabled','true');
10991 initEvents : function()
10994 this.inputEl().on("keydown" , this.fireKey, this);
10995 this.inputEl().on("focus", this.onFocus, this);
10996 this.inputEl().on("blur", this.onBlur, this);
10998 this.inputEl().relayEvent('keyup', this);
11000 this.indicator = this.indicatorEl();
11002 if(this.indicator){
11003 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11006 // reference to original value for reset
11007 this.originalValue = this.getValue();
11008 //Roo.form.TextField.superclass.initEvents.call(this);
11009 if(this.validationEvent == 'keyup'){
11010 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011 this.inputEl().on('keyup', this.filterValidation, this);
11013 else if(this.validationEvent !== false){
11014 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11017 if(this.selectOnFocus){
11018 this.on("focus", this.preFocus, this);
11021 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022 this.inputEl().on("keypress", this.filterKeys, this);
11024 this.inputEl().relayEvent('keypress', this);
11027 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11028 this.el.on("click", this.autoSize, this);
11031 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11035 if (typeof(this.before) == 'object') {
11036 this.before.render(this.el.select('.roo-input-before',true).first());
11038 if (typeof(this.after) == 'object') {
11039 this.after.render(this.el.select('.roo-input-after',true).first());
11042 this.inputEl().on('change', this.onChange, this);
11045 filterValidation : function(e){
11046 if(!e.isNavKeyPress()){
11047 this.validationTask.delay(this.validationDelay);
11051 * Validates the field value
11052 * @return {Boolean} True if the value is valid, else false
11054 validate : function(){
11055 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056 if(this.disabled || this.validateValue(this.getRawValue())){
11061 this.markInvalid();
11067 * Validates a value according to the field's validation rules and marks the field as invalid
11068 * if the validation fails
11069 * @param {Mixed} value The value to validate
11070 * @return {Boolean} True if the value is valid, else false
11072 validateValue : function(value)
11074 if(this.getVisibilityEl().hasClass('hidden')){
11078 if(value.length < 1) { // if it's blank
11079 if(this.allowBlank){
11085 if(value.length < this.minLength){
11088 if(value.length > this.maxLength){
11092 var vt = Roo.form.VTypes;
11093 if(!vt[this.vtype](value, this)){
11097 if(typeof this.validator == "function"){
11098 var msg = this.validator(value);
11102 if (typeof(msg) == 'string') {
11103 this.invalidText = msg;
11107 if(this.regex && !this.regex.test(value)){
11115 fireKey : function(e){
11116 //Roo.log('field ' + e.getKey());
11117 if(e.isNavKeyPress()){
11118 this.fireEvent("specialkey", this, e);
11121 focus : function (selectText){
11123 this.inputEl().focus();
11124 if(selectText === true){
11125 this.inputEl().dom.select();
11131 onFocus : function(){
11132 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133 // this.el.addClass(this.focusClass);
11135 if(!this.hasFocus){
11136 this.hasFocus = true;
11137 this.startValue = this.getValue();
11138 this.fireEvent("focus", this);
11142 beforeBlur : Roo.emptyFn,
11146 onBlur : function(){
11148 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149 //this.el.removeClass(this.focusClass);
11151 this.hasFocus = false;
11152 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11155 var v = this.getValue();
11156 if(String(v) !== String(this.startValue)){
11157 this.fireEvent('change', this, v, this.startValue);
11159 this.fireEvent("blur", this);
11162 onChange : function(e)
11164 var v = this.getValue();
11165 if(String(v) !== String(this.startValue)){
11166 this.fireEvent('change', this, v, this.startValue);
11172 * Resets the current field value to the originally loaded value and clears any validation messages
11174 reset : function(){
11175 this.setValue(this.originalValue);
11179 * Returns the name of the field
11180 * @return {Mixed} name The name field
11182 getName: function(){
11186 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11187 * @return {Mixed} value The field value
11189 getValue : function(){
11191 var v = this.inputEl().getValue();
11196 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11197 * @return {Mixed} value The field value
11199 getRawValue : function(){
11200 var v = this.inputEl().getValue();
11206 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11207 * @param {Mixed} value The value to set
11209 setRawValue : function(v){
11210 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11213 selectText : function(start, end){
11214 var v = this.getRawValue();
11216 start = start === undefined ? 0 : start;
11217 end = end === undefined ? v.length : end;
11218 var d = this.inputEl().dom;
11219 if(d.setSelectionRange){
11220 d.setSelectionRange(start, end);
11221 }else if(d.createTextRange){
11222 var range = d.createTextRange();
11223 range.moveStart("character", start);
11224 range.moveEnd("character", v.length-end);
11231 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11232 * @param {Mixed} value The value to set
11234 setValue : function(v){
11237 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11243 processValue : function(value){
11244 if(this.stripCharsRe){
11245 var newValue = value.replace(this.stripCharsRe, '');
11246 if(newValue !== value){
11247 this.setRawValue(newValue);
11254 preFocus : function(){
11256 if(this.selectOnFocus){
11257 this.inputEl().dom.select();
11260 filterKeys : function(e){
11261 var k = e.getKey();
11262 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11265 var c = e.getCharCode(), cc = String.fromCharCode(c);
11266 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11269 if(!this.maskRe.test(cc)){
11274 * Clear any invalid styles/messages for this field
11276 clearInvalid : function(){
11278 if(!this.el || this.preventMark){ // not rendered
11283 this.el.removeClass([this.invalidClass, 'is-invalid']);
11285 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11287 var feedback = this.el.select('.form-control-feedback', true).first();
11290 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11295 if(this.indicator){
11296 this.indicator.removeClass('visible');
11297 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11300 this.fireEvent('valid', this);
11304 * Mark this field as valid
11306 markValid : function()
11308 if(!this.el || this.preventMark){ // not rendered...
11312 this.el.removeClass([this.invalidClass, this.validClass]);
11313 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11315 var feedback = this.el.select('.form-control-feedback', true).first();
11318 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11321 if(this.indicator){
11322 this.indicator.removeClass('visible');
11323 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11331 if(this.allowBlank && !this.getRawValue().length){
11334 if (Roo.bootstrap.version == 3) {
11335 this.el.addClass(this.validClass);
11337 this.inputEl().addClass('is-valid');
11340 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11342 var feedback = this.el.select('.form-control-feedback', true).first();
11345 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11351 this.fireEvent('valid', this);
11355 * Mark this field as invalid
11356 * @param {String} msg The validation message
11358 markInvalid : function(msg)
11360 if(!this.el || this.preventMark){ // not rendered
11364 this.el.removeClass([this.invalidClass, this.validClass]);
11365 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11367 var feedback = this.el.select('.form-control-feedback', true).first();
11370 this.el.select('.form-control-feedback', true).first().removeClass(
11371 [this.invalidFeedbackClass, this.validFeedbackClass]);
11378 if(this.allowBlank && !this.getRawValue().length){
11382 if(this.indicator){
11383 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384 this.indicator.addClass('visible');
11386 if (Roo.bootstrap.version == 3) {
11387 this.el.addClass(this.invalidClass);
11389 this.inputEl().addClass('is-invalid');
11394 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11396 var feedback = this.el.select('.form-control-feedback', true).first();
11399 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11401 if(this.getValue().length || this.forceFeedback){
11402 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11409 this.fireEvent('invalid', this, msg);
11412 SafariOnKeyDown : function(event)
11414 // this is a workaround for a password hang bug on chrome/ webkit.
11415 if (this.inputEl().dom.type != 'password') {
11419 var isSelectAll = false;
11421 if(this.inputEl().dom.selectionEnd > 0){
11422 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11424 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425 event.preventDefault();
11430 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11432 event.preventDefault();
11433 // this is very hacky as keydown always get's upper case.
11435 var cc = String.fromCharCode(event.getCharCode());
11436 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11440 adjustWidth : function(tag, w){
11441 tag = tag.toLowerCase();
11442 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444 if(tag == 'input'){
11447 if(tag == 'textarea'){
11450 }else if(Roo.isOpera){
11451 if(tag == 'input'){
11454 if(tag == 'textarea'){
11462 setFieldLabel : function(v)
11464 if(!this.rendered){
11468 if(this.indicatorEl()){
11469 var ar = this.el.select('label > span',true);
11471 if (ar.elements.length) {
11472 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473 this.fieldLabel = v;
11477 var br = this.el.select('label',true);
11479 if(br.elements.length) {
11480 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481 this.fieldLabel = v;
11485 Roo.log('Cannot Found any of label > span || label in input');
11489 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490 this.fieldLabel = v;
11505 * @class Roo.bootstrap.TextArea
11506 * @extends Roo.bootstrap.Input
11507 * Bootstrap TextArea class
11508 * @cfg {Number} cols Specifies the visible width of a text area
11509 * @cfg {Number} rows Specifies the visible number of lines in a text area
11510 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512 * @cfg {string} html text
11515 * Create a new TextArea
11516 * @param {Object} config The config object
11519 Roo.bootstrap.TextArea = function(config){
11520 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11534 getAutoCreate : function(){
11536 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11542 if(this.inputType != 'hidden'){
11543 cfg.cls = 'form-group' //input-group
11551 value : this.value || '',
11552 html: this.html || '',
11553 cls : 'form-control',
11554 placeholder : this.placeholder || ''
11558 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559 input.maxLength = this.maxLength;
11563 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11567 input.cols = this.cols;
11570 if (this.readOnly) {
11571 input.readonly = true;
11575 input.name = this.name;
11579 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11583 ['xs','sm','md','lg'].map(function(size){
11584 if (settings[size]) {
11585 cfg.cls += ' col-' + size + '-' + settings[size];
11589 var inputblock = input;
11591 if(this.hasFeedback && !this.allowBlank){
11595 cls: 'glyphicon form-control-feedback'
11599 cls : 'has-feedback',
11608 if (this.before || this.after) {
11611 cls : 'input-group',
11615 inputblock.cn.push({
11617 cls : 'input-group-addon',
11622 inputblock.cn.push(input);
11624 if(this.hasFeedback && !this.allowBlank){
11625 inputblock.cls += ' has-feedback';
11626 inputblock.cn.push(feedback);
11630 inputblock.cn.push({
11632 cls : 'input-group-addon',
11639 if (align ==='left' && this.fieldLabel.length) {
11644 cls : 'control-label',
11645 html : this.fieldLabel
11656 if(this.labelWidth > 12){
11657 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11660 if(this.labelWidth < 13 && this.labelmd == 0){
11661 this.labelmd = this.labelWidth;
11664 if(this.labellg > 0){
11665 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11669 if(this.labelmd > 0){
11670 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11674 if(this.labelsm > 0){
11675 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11679 if(this.labelxs > 0){
11680 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11684 } else if ( this.fieldLabel.length) {
11689 //cls : 'input-group-addon',
11690 html : this.fieldLabel
11708 if (this.disabled) {
11709 input.disabled=true;
11716 * return the real textarea element.
11718 inputEl: function ()
11720 return this.el.select('textarea.form-control',true).first();
11724 * Clear any invalid styles/messages for this field
11726 clearInvalid : function()
11729 if(!this.el || this.preventMark){ // not rendered
11733 var label = this.el.select('label', true).first();
11734 var icon = this.el.select('i.fa-star', true).first();
11739 this.el.removeClass( this.validClass);
11740 this.inputEl().removeClass('is-invalid');
11742 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11744 var feedback = this.el.select('.form-control-feedback', true).first();
11747 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11752 this.fireEvent('valid', this);
11756 * Mark this field as valid
11758 markValid : function()
11760 if(!this.el || this.preventMark){ // not rendered
11764 this.el.removeClass([this.invalidClass, this.validClass]);
11765 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11767 var feedback = this.el.select('.form-control-feedback', true).first();
11770 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11773 if(this.disabled || this.allowBlank){
11777 var label = this.el.select('label', true).first();
11778 var icon = this.el.select('i.fa-star', true).first();
11783 if (Roo.bootstrap.version == 3) {
11784 this.el.addClass(this.validClass);
11786 this.inputEl().addClass('is-valid');
11790 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11792 var feedback = this.el.select('.form-control-feedback', true).first();
11795 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11801 this.fireEvent('valid', this);
11805 * Mark this field as invalid
11806 * @param {String} msg The validation message
11808 markInvalid : function(msg)
11810 if(!this.el || this.preventMark){ // not rendered
11814 this.el.removeClass([this.invalidClass, this.validClass]);
11815 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11817 var feedback = this.el.select('.form-control-feedback', true).first();
11820 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11823 if(this.disabled || this.allowBlank){
11827 var label = this.el.select('label', true).first();
11828 var icon = this.el.select('i.fa-star', true).first();
11830 if(!this.getValue().length && label && !icon){
11831 this.el.createChild({
11833 cls : 'text-danger fa fa-lg fa-star',
11834 tooltip : 'This field is required',
11835 style : 'margin-right:5px;'
11839 if (Roo.bootstrap.version == 3) {
11840 this.el.addClass(this.invalidClass);
11842 this.inputEl().addClass('is-invalid');
11845 // fixme ... this may be depricated need to test..
11846 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11848 var feedback = this.el.select('.form-control-feedback', true).first();
11851 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11853 if(this.getValue().length || this.forceFeedback){
11854 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11861 this.fireEvent('invalid', this, msg);
11869 * trigger field - base class for combo..
11874 * @class Roo.bootstrap.TriggerField
11875 * @extends Roo.bootstrap.Input
11876 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879 * for which you can provide a custom implementation. For example:
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11886 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11889 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11893 * Create a new TriggerField.
11894 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895 * to the base TextField)
11897 Roo.bootstrap.TriggerField = function(config){
11898 this.mimicing = false;
11899 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11904 * @cfg {String} triggerClass A CSS class to apply to the trigger
11907 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11912 * @cfg {Boolean} removable (true|false) special filter default false
11916 /** @cfg {Boolean} grow @hide */
11917 /** @cfg {Number} growMin @hide */
11918 /** @cfg {Number} growMax @hide */
11924 autoSize: Roo.emptyFn,
11928 deferHeight : true,
11931 actionMode : 'wrap',
11936 getAutoCreate : function(){
11938 var align = this.labelAlign || this.parentLabelAlign();
11943 cls: 'form-group' //input-group
11950 type : this.inputType,
11951 cls : 'form-control',
11952 autocomplete: 'new-password',
11953 placeholder : this.placeholder || ''
11957 input.name = this.name;
11960 input.cls += ' input-' + this.size;
11963 if (this.disabled) {
11964 input.disabled=true;
11967 var inputblock = input;
11969 if(this.hasFeedback && !this.allowBlank){
11973 cls: 'glyphicon form-control-feedback'
11976 if(this.removable && !this.editable ){
11978 cls : 'has-feedback',
11984 cls : 'roo-combo-removable-btn close'
11991 cls : 'has-feedback',
12000 if(this.removable && !this.editable ){
12002 cls : 'roo-removable',
12008 cls : 'roo-combo-removable-btn close'
12015 if (this.before || this.after) {
12018 cls : 'input-group',
12022 inputblock.cn.push({
12024 cls : 'input-group-addon input-group-prepend input-group-text',
12029 inputblock.cn.push(input);
12031 if(this.hasFeedback && !this.allowBlank){
12032 inputblock.cls += ' has-feedback';
12033 inputblock.cn.push(feedback);
12037 inputblock.cn.push({
12039 cls : 'input-group-addon input-group-append input-group-text',
12048 var ibwrap = inputblock;
12053 cls: 'roo-select2-choices',
12057 cls: 'roo-select2-search-field',
12069 cls: 'roo-select2-container input-group',
12074 cls: 'form-hidden-field'
12080 if(!this.multiple && this.showToggleBtn){
12086 if (this.caret != false) {
12089 cls: 'fa fa-' + this.caret
12096 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12098 Roo.bootstrap.version == 3 ? caret : '',
12101 cls: 'combobox-clear',
12115 combobox.cls += ' roo-select2-container-multi';
12119 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120 tooltip : 'This field is required'
12122 if (Roo.bootstrap.version == 4) {
12125 style : 'display:none'
12130 if (align ==='left' && this.fieldLabel.length) {
12132 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12139 cls : 'control-label',
12140 html : this.fieldLabel
12152 var labelCfg = cfg.cn[1];
12153 var contentCfg = cfg.cn[2];
12155 if(this.indicatorpos == 'right'){
12160 cls : 'control-label',
12164 html : this.fieldLabel
12178 labelCfg = cfg.cn[0];
12179 contentCfg = cfg.cn[1];
12182 if(this.labelWidth > 12){
12183 labelCfg.style = "width: " + this.labelWidth + 'px';
12186 if(this.labelWidth < 13 && this.labelmd == 0){
12187 this.labelmd = this.labelWidth;
12190 if(this.labellg > 0){
12191 labelCfg.cls += ' col-lg-' + this.labellg;
12192 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12195 if(this.labelmd > 0){
12196 labelCfg.cls += ' col-md-' + this.labelmd;
12197 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12200 if(this.labelsm > 0){
12201 labelCfg.cls += ' col-sm-' + this.labelsm;
12202 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12205 if(this.labelxs > 0){
12206 labelCfg.cls += ' col-xs-' + this.labelxs;
12207 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12210 } else if ( this.fieldLabel.length) {
12211 // Roo.log(" label");
12216 //cls : 'input-group-addon',
12217 html : this.fieldLabel
12225 if(this.indicatorpos == 'right'){
12233 html : this.fieldLabel
12247 // Roo.log(" no label && no align");
12254 ['xs','sm','md','lg'].map(function(size){
12255 if (settings[size]) {
12256 cfg.cls += ' col-' + size + '-' + settings[size];
12267 onResize : function(w, h){
12268 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 // if(typeof w == 'number'){
12270 // var x = w - this.trigger.getWidth();
12271 // this.inputEl().setWidth(this.adjustWidth('input', x));
12272 // this.trigger.setStyle('left', x+'px');
12277 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12280 getResizeEl : function(){
12281 return this.inputEl();
12285 getPositionEl : function(){
12286 return this.inputEl();
12290 alignErrorIcon : function(){
12291 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12295 initEvents : function(){
12299 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301 if(!this.multiple && this.showToggleBtn){
12302 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303 if(this.hideTrigger){
12304 this.trigger.setDisplayed(false);
12306 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12310 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12313 if(this.removable && !this.editable && !this.tickable){
12314 var close = this.closeTriggerEl();
12317 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318 close.on('click', this.removeBtnClick, this, close);
12322 //this.trigger.addClassOnOver('x-form-trigger-over');
12323 //this.trigger.addClassOnClick('x-form-trigger-click');
12326 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12330 closeTriggerEl : function()
12332 var close = this.el.select('.roo-combo-removable-btn', true).first();
12333 return close ? close : false;
12336 removeBtnClick : function(e, h, el)
12338 e.preventDefault();
12340 if(this.fireEvent("remove", this) !== false){
12342 this.fireEvent("afterremove", this)
12346 createList : function()
12348 this.list = Roo.get(document.body).createChild({
12349 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350 cls: 'typeahead typeahead-long dropdown-menu shadow',
12351 style: 'display:none'
12354 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12359 initTrigger : function(){
12364 onDestroy : function(){
12366 this.trigger.removeAllListeners();
12367 // this.trigger.remove();
12370 // this.wrap.remove();
12372 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12376 onFocus : function(){
12377 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12379 if(!this.mimicing){
12380 this.wrap.addClass('x-trigger-wrap-focus');
12381 this.mimicing = true;
12382 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383 if(this.monitorTab){
12384 this.el.on("keydown", this.checkTab, this);
12391 checkTab : function(e){
12392 if(e.getKey() == e.TAB){
12393 this.triggerBlur();
12398 onBlur : function(){
12403 mimicBlur : function(e, t){
12405 if(!this.wrap.contains(t) && this.validateBlur()){
12406 this.triggerBlur();
12412 triggerBlur : function(){
12413 this.mimicing = false;
12414 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415 if(this.monitorTab){
12416 this.el.un("keydown", this.checkTab, this);
12418 //this.wrap.removeClass('x-trigger-wrap-focus');
12419 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12423 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424 validateBlur : function(e, t){
12429 onDisable : function(){
12430 this.inputEl().dom.disabled = true;
12431 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12433 // this.wrap.addClass('x-item-disabled');
12438 onEnable : function(){
12439 this.inputEl().dom.disabled = false;
12440 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12442 // this.el.removeClass('x-item-disabled');
12447 onShow : function(){
12448 var ae = this.getActionEl();
12451 ae.dom.style.display = '';
12452 ae.dom.style.visibility = 'visible';
12458 onHide : function(){
12459 var ae = this.getActionEl();
12460 ae.dom.style.display = 'none';
12464 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12465 * by an implementing function.
12467 * @param {EventObject} e
12469 onTriggerClick : Roo.emptyFn
12477 * @class Roo.bootstrap.CardUploader
12478 * @extends Roo.bootstrap.Button
12479 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480 * @cfg {Number} errorTimeout default 3000
12481 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12482 * @cfg {Array} html The button text.
12486 * Create a new CardUploader
12487 * @param {Object} config The config object
12490 Roo.bootstrap.CardUploader = function(config){
12494 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12497 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12507 errorTimeout : 3000,
12511 fileCollection : false,
12514 getAutoCreate : function()
12518 cls :'form-group' ,
12523 //cls : 'input-group-addon',
12524 html : this.fieldLabel
12532 value : this.value,
12533 cls : 'd-none form-control'
12538 multiple : 'multiple',
12540 cls : 'd-none roo-card-upload-selector'
12544 cls : 'roo-card-uploader-button-container w-100 mb-2'
12547 cls : 'card-columns roo-card-uploader-container'
12557 getChildContainer : function() /// what children are added to.
12559 return this.containerEl;
12562 getButtonContainer : function() /// what children are added to.
12564 return this.el.select(".roo-card-uploader-button-container").first();
12567 initEvents : function()
12570 Roo.bootstrap.Input.prototype.initEvents.call(this);
12574 xns: Roo.bootstrap,
12577 container_method : 'getButtonContainer' ,
12578 html : this.html, // fix changable?
12581 'click' : function(btn, e) {
12590 this.urlAPI = (window.createObjectURL && window) ||
12591 (window.URL && URL.revokeObjectURL && URL) ||
12592 (window.webkitURL && webkitURL);
12597 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12599 this.selectorEl.on('change', this.onFileSelected, this);
12602 this.images.forEach(function(img) {
12605 this.images = false;
12607 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12613 onClick : function(e)
12615 e.preventDefault();
12617 this.selectorEl.dom.click();
12621 onFileSelected : function(e)
12623 e.preventDefault();
12625 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12629 Roo.each(this.selectorEl.dom.files, function(file){
12630 this.addFile(file);
12639 addFile : function(file)
12642 if(typeof(file) === 'string'){
12643 throw "Add file by name?"; // should not happen
12647 if(!file || !this.urlAPI){
12657 var url = _this.urlAPI.createObjectURL( file);
12660 id : Roo.bootstrap.CardUploader.ID--,
12661 is_uploaded : false,
12664 mimetype : file.type,
12671 addCard : function (data)
12673 // hidden input element?
12674 // if the file is not an image...
12675 //then we need to use something other that and header_image
12680 xns : Roo.bootstrap,
12681 xtype : 'CardFooter',
12684 xns : Roo.bootstrap,
12690 xns : Roo.bootstrap,
12692 html : String.format("<small>{0}</small>", data.title),
12693 cls : 'col-11 text-left',
12698 click : function() {
12699 this.downloadCard(data.id)
12705 xns : Roo.bootstrap,
12713 click : function() {
12714 t.removeCard(data.id)
12726 var cn = this.addxtype(
12729 xns : Roo.bootstrap,
12732 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12733 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12734 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12739 initEvents : function() {
12740 Roo.bootstrap.Card.prototype.initEvents.call(this);
12741 this.imgEl = this.el.select('.card-img-top').first();
12743 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12744 this.imgEl.set({ 'pointer' : 'cursor' });
12753 // dont' really need ot update items.
12754 // this.items.push(cn);
12755 this.fileCollection.add(cn);
12756 this.updateInput();
12759 removeCard : function(id)
12762 var card = this.fileCollection.get(id);
12763 card.data.is_deleted = 1;
12764 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12765 this.fileCollection.remove(card);
12766 //this.items = this.items.filter(function(e) { return e != card });
12767 // dont' really need ot update items.
12768 card.el.dom.parentNode.removeChild(card.el.dom);
12773 this.fileCollection.each(function(card) {
12774 card.el.dom.parentNode.removeChild(card.el.dom);
12776 this.fileCollection.clear();
12777 this.updateInput();
12780 updateInput : function()
12784 var dom = this.inputEl().dom;
12785 var fc = this.fileCollection;
12786 var next = function() {
12787 if (i >= fc.length) {
12788 dom.value = JSON.stringify(data);
12791 var reader = new FileReader();
12792 reader.onloadend = function(evt) {
12794 var ee = Roo.apply({}, fc[i]);
12795 ee.src = evt.target.result;
12800 reader.readAsDataURL(fc[i].src);
12812 Roo.bootstrap.CardUploader.ID = -1;/*
12814 * Ext JS Library 1.1.1
12815 * Copyright(c) 2006-2007, Ext JS, LLC.
12817 * Originally Released Under LGPL - original licence link has changed is not relivant.
12820 * <script type="text/javascript">
12825 * @class Roo.data.SortTypes
12827 * Defines the default sorting (casting?) comparison functions used when sorting data.
12829 Roo.data.SortTypes = {
12831 * Default sort that does nothing
12832 * @param {Mixed} s The value being converted
12833 * @return {Mixed} The comparison value
12835 none : function(s){
12840 * The regular expression used to strip tags
12844 stripTagsRE : /<\/?[^>]+>/gi,
12847 * Strips all HTML tags to sort on text only
12848 * @param {Mixed} s The value being converted
12849 * @return {String} The comparison value
12851 asText : function(s){
12852 return String(s).replace(this.stripTagsRE, "");
12856 * Strips all HTML tags to sort on text only - Case insensitive
12857 * @param {Mixed} s The value being converted
12858 * @return {String} The comparison value
12860 asUCText : function(s){
12861 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12865 * Case insensitive string
12866 * @param {Mixed} s The value being converted
12867 * @return {String} The comparison value
12869 asUCString : function(s) {
12870 return String(s).toUpperCase();
12875 * @param {Mixed} s The value being converted
12876 * @return {Number} The comparison value
12878 asDate : function(s) {
12882 if(s instanceof Date){
12883 return s.getTime();
12885 return Date.parse(String(s));
12890 * @param {Mixed} s The value being converted
12891 * @return {Float} The comparison value
12893 asFloat : function(s) {
12894 var val = parseFloat(String(s).replace(/,/g, ""));
12903 * @param {Mixed} s The value being converted
12904 * @return {Number} The comparison value
12906 asInt : function(s) {
12907 var val = parseInt(String(s).replace(/,/g, ""));
12915 * Ext JS Library 1.1.1
12916 * Copyright(c) 2006-2007, Ext JS, LLC.
12918 * Originally Released Under LGPL - original licence link has changed is not relivant.
12921 * <script type="text/javascript">
12925 * @class Roo.data.Record
12926 * Instances of this class encapsulate both record <em>definition</em> information, and record
12927 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12928 * to access Records cached in an {@link Roo.data.Store} object.<br>
12930 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12931 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12934 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12936 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12937 * {@link #create}. The parameters are the same.
12938 * @param {Array} data An associative Array of data values keyed by the field name.
12939 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12940 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12941 * not specified an integer id is generated.
12943 Roo.data.Record = function(data, id){
12944 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12949 * Generate a constructor for a specific record layout.
12950 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12951 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12952 * Each field definition object may contain the following properties: <ul>
12953 * <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,
12954 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12955 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12956 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12957 * is being used, then this is a string containing the javascript expression to reference the data relative to
12958 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12959 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12960 * this may be omitted.</p></li>
12961 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12962 * <ul><li>auto (Default, implies no conversion)</li>
12967 * <li>date</li></ul></p></li>
12968 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12969 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12970 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12971 * by the Reader into an object that will be stored in the Record. It is passed the
12972 * following parameters:<ul>
12973 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12975 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12977 * <br>usage:<br><pre><code>
12978 var TopicRecord = Roo.data.Record.create(
12979 {name: 'title', mapping: 'topic_title'},
12980 {name: 'author', mapping: 'username'},
12981 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12982 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12983 {name: 'lastPoster', mapping: 'user2'},
12984 {name: 'excerpt', mapping: 'post_text'}
12987 var myNewRecord = new TopicRecord({
12988 title: 'Do my job please',
12991 lastPost: new Date(),
12992 lastPoster: 'Animal',
12993 excerpt: 'No way dude!'
12995 myStore.add(myNewRecord);
13000 Roo.data.Record.create = function(o){
13001 var f = function(){
13002 f.superclass.constructor.apply(this, arguments);
13004 Roo.extend(f, Roo.data.Record);
13005 var p = f.prototype;
13006 p.fields = new Roo.util.MixedCollection(false, function(field){
13009 for(var i = 0, len = o.length; i < len; i++){
13010 p.fields.add(new Roo.data.Field(o[i]));
13012 f.getField = function(name){
13013 return p.fields.get(name);
13018 Roo.data.Record.AUTO_ID = 1000;
13019 Roo.data.Record.EDIT = 'edit';
13020 Roo.data.Record.REJECT = 'reject';
13021 Roo.data.Record.COMMIT = 'commit';
13023 Roo.data.Record.prototype = {
13025 * Readonly flag - true if this record has been modified.
13034 join : function(store){
13035 this.store = store;
13039 * Set the named field to the specified value.
13040 * @param {String} name The name of the field to set.
13041 * @param {Object} value The value to set the field to.
13043 set : function(name, value){
13044 if(this.data[name] == value){
13048 if(!this.modified){
13049 this.modified = {};
13051 if(typeof this.modified[name] == 'undefined'){
13052 this.modified[name] = this.data[name];
13054 this.data[name] = value;
13055 if(!this.editing && this.store){
13056 this.store.afterEdit(this);
13061 * Get the value of the named field.
13062 * @param {String} name The name of the field to get the value of.
13063 * @return {Object} The value of the field.
13065 get : function(name){
13066 return this.data[name];
13070 beginEdit : function(){
13071 this.editing = true;
13072 this.modified = {};
13076 cancelEdit : function(){
13077 this.editing = false;
13078 delete this.modified;
13082 endEdit : function(){
13083 this.editing = false;
13084 if(this.dirty && this.store){
13085 this.store.afterEdit(this);
13090 * Usually called by the {@link Roo.data.Store} which owns the Record.
13091 * Rejects all changes made to the Record since either creation, or the last commit operation.
13092 * Modified fields are reverted to their original values.
13094 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13095 * of reject operations.
13097 reject : function(){
13098 var m = this.modified;
13100 if(typeof m[n] != "function"){
13101 this.data[n] = m[n];
13104 this.dirty = false;
13105 delete this.modified;
13106 this.editing = false;
13108 this.store.afterReject(this);
13113 * Usually called by the {@link Roo.data.Store} which owns the Record.
13114 * Commits all changes made to the Record since either creation, or the last commit operation.
13116 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13117 * of commit operations.
13119 commit : function(){
13120 this.dirty = false;
13121 delete this.modified;
13122 this.editing = false;
13124 this.store.afterCommit(this);
13129 hasError : function(){
13130 return this.error != null;
13134 clearError : function(){
13139 * Creates a copy of this record.
13140 * @param {String} id (optional) A new record id if you don't want to use this record's id
13143 copy : function(newId) {
13144 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13148 * Ext JS Library 1.1.1
13149 * Copyright(c) 2006-2007, Ext JS, LLC.
13151 * Originally Released Under LGPL - original licence link has changed is not relivant.
13154 * <script type="text/javascript">
13160 * @class Roo.data.Store
13161 * @extends Roo.util.Observable
13162 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13163 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13165 * 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
13166 * has no knowledge of the format of the data returned by the Proxy.<br>
13168 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13169 * instances from the data object. These records are cached and made available through accessor functions.
13171 * Creates a new Store.
13172 * @param {Object} config A config object containing the objects needed for the Store to access data,
13173 * and read the data into Records.
13175 Roo.data.Store = function(config){
13176 this.data = new Roo.util.MixedCollection(false);
13177 this.data.getKey = function(o){
13180 this.baseParams = {};
13182 this.paramNames = {
13187 "multisort" : "_multisort"
13190 if(config && config.data){
13191 this.inlineData = config.data;
13192 delete config.data;
13195 Roo.apply(this, config);
13197 if(this.reader){ // reader passed
13198 this.reader = Roo.factory(this.reader, Roo.data);
13199 this.reader.xmodule = this.xmodule || false;
13200 if(!this.recordType){
13201 this.recordType = this.reader.recordType;
13203 if(this.reader.onMetaChange){
13204 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13208 if(this.recordType){
13209 this.fields = this.recordType.prototype.fields;
13211 this.modified = [];
13215 * @event datachanged
13216 * Fires when the data cache has changed, and a widget which is using this Store
13217 * as a Record cache should refresh its view.
13218 * @param {Store} this
13220 datachanged : true,
13222 * @event metachange
13223 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13224 * @param {Store} this
13225 * @param {Object} meta The JSON metadata
13230 * Fires when Records have been added to the Store
13231 * @param {Store} this
13232 * @param {Roo.data.Record[]} records The array of Records added
13233 * @param {Number} index The index at which the record(s) were added
13238 * Fires when a Record has been removed from the Store
13239 * @param {Store} this
13240 * @param {Roo.data.Record} record The Record that was removed
13241 * @param {Number} index The index at which the record was removed
13246 * Fires when a Record has been updated
13247 * @param {Store} this
13248 * @param {Roo.data.Record} record The Record that was updated
13249 * @param {String} operation The update operation being performed. Value may be one of:
13251 Roo.data.Record.EDIT
13252 Roo.data.Record.REJECT
13253 Roo.data.Record.COMMIT
13259 * Fires when the data cache has been cleared.
13260 * @param {Store} this
13264 * @event beforeload
13265 * Fires before a request is made for a new data object. If the beforeload handler returns false
13266 * the load action will be canceled.
13267 * @param {Store} this
13268 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13272 * @event beforeloadadd
13273 * Fires after a new set of Records has been loaded.
13274 * @param {Store} this
13275 * @param {Roo.data.Record[]} records The Records that were loaded
13276 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13278 beforeloadadd : true,
13281 * Fires after a new set of Records has been loaded, before they are added to the store.
13282 * @param {Store} this
13283 * @param {Roo.data.Record[]} records The Records that were loaded
13284 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13285 * @params {Object} return from reader
13289 * @event loadexception
13290 * Fires if an exception occurs in the Proxy during loading.
13291 * Called with the signature of the Proxy's "loadexception" event.
13292 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13295 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13296 * @param {Object} load options
13297 * @param {Object} jsonData from your request (normally this contains the Exception)
13299 loadexception : true
13303 this.proxy = Roo.factory(this.proxy, Roo.data);
13304 this.proxy.xmodule = this.xmodule || false;
13305 this.relayEvents(this.proxy, ["loadexception"]);
13307 this.sortToggle = {};
13308 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13310 Roo.data.Store.superclass.constructor.call(this);
13312 if(this.inlineData){
13313 this.loadData(this.inlineData);
13314 delete this.inlineData;
13318 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13320 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13321 * without a remote query - used by combo/forms at present.
13325 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13328 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13331 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13332 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13335 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13336 * on any HTTP request
13339 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13342 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13346 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13347 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13349 remoteSort : false,
13352 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13353 * loaded or when a record is removed. (defaults to false).
13355 pruneModifiedRecords : false,
13358 lastOptions : null,
13361 * Add Records to the Store and fires the add event.
13362 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13364 add : function(records){
13365 records = [].concat(records);
13366 for(var i = 0, len = records.length; i < len; i++){
13367 records[i].join(this);
13369 var index = this.data.length;
13370 this.data.addAll(records);
13371 this.fireEvent("add", this, records, index);
13375 * Remove a Record from the Store and fires the remove event.
13376 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13378 remove : function(record){
13379 var index = this.data.indexOf(record);
13380 this.data.removeAt(index);
13382 if(this.pruneModifiedRecords){
13383 this.modified.remove(record);
13385 this.fireEvent("remove", this, record, index);
13389 * Remove all Records from the Store and fires the clear event.
13391 removeAll : function(){
13393 if(this.pruneModifiedRecords){
13394 this.modified = [];
13396 this.fireEvent("clear", this);
13400 * Inserts Records to the Store at the given index and fires the add event.
13401 * @param {Number} index The start index at which to insert the passed Records.
13402 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13404 insert : function(index, records){
13405 records = [].concat(records);
13406 for(var i = 0, len = records.length; i < len; i++){
13407 this.data.insert(index, records[i]);
13408 records[i].join(this);
13410 this.fireEvent("add", this, records, index);
13414 * Get the index within the cache of the passed Record.
13415 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13416 * @return {Number} The index of the passed Record. Returns -1 if not found.
13418 indexOf : function(record){
13419 return this.data.indexOf(record);
13423 * Get the index within the cache of the Record with the passed id.
13424 * @param {String} id The id of the Record to find.
13425 * @return {Number} The index of the Record. Returns -1 if not found.
13427 indexOfId : function(id){
13428 return this.data.indexOfKey(id);
13432 * Get the Record with the specified id.
13433 * @param {String} id The id of the Record to find.
13434 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13436 getById : function(id){
13437 return this.data.key(id);
13441 * Get the Record at the specified index.
13442 * @param {Number} index The index of the Record to find.
13443 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13445 getAt : function(index){
13446 return this.data.itemAt(index);
13450 * Returns a range of Records between specified indices.
13451 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13452 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13453 * @return {Roo.data.Record[]} An array of Records
13455 getRange : function(start, end){
13456 return this.data.getRange(start, end);
13460 storeOptions : function(o){
13461 o = Roo.apply({}, o);
13464 this.lastOptions = o;
13468 * Loads the Record cache from the configured Proxy using the configured Reader.
13470 * If using remote paging, then the first load call must specify the <em>start</em>
13471 * and <em>limit</em> properties in the options.params property to establish the initial
13472 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13474 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13475 * and this call will return before the new data has been loaded. Perform any post-processing
13476 * in a callback function, or in a "load" event handler.</strong>
13478 * @param {Object} options An object containing properties which control loading options:<ul>
13479 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13480 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13481 * passed the following arguments:<ul>
13482 * <li>r : Roo.data.Record[]</li>
13483 * <li>options: Options object from the load call</li>
13484 * <li>success: Boolean success indicator</li></ul></li>
13485 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13486 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13489 load : function(options){
13490 options = options || {};
13491 if(this.fireEvent("beforeload", this, options) !== false){
13492 this.storeOptions(options);
13493 var p = Roo.apply(options.params || {}, this.baseParams);
13494 // if meta was not loaded from remote source.. try requesting it.
13495 if (!this.reader.metaFromRemote) {
13496 p._requestMeta = 1;
13498 if(this.sortInfo && this.remoteSort){
13499 var pn = this.paramNames;
13500 p[pn["sort"]] = this.sortInfo.field;
13501 p[pn["dir"]] = this.sortInfo.direction;
13503 if (this.multiSort) {
13504 var pn = this.paramNames;
13505 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13508 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13513 * Reloads the Record cache from the configured Proxy using the configured Reader and
13514 * the options from the last load operation performed.
13515 * @param {Object} options (optional) An object containing properties which may override the options
13516 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13517 * the most recently used options are reused).
13519 reload : function(options){
13520 this.load(Roo.applyIf(options||{}, this.lastOptions));
13524 // Called as a callback by the Reader during a load operation.
13525 loadRecords : function(o, options, success){
13526 if(!o || success === false){
13527 if(success !== false){
13528 this.fireEvent("load", this, [], options, o);
13530 if(options.callback){
13531 options.callback.call(options.scope || this, [], options, false);
13535 // if data returned failure - throw an exception.
13536 if (o.success === false) {
13537 // show a message if no listener is registered.
13538 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13539 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13541 // loadmask wil be hooked into this..
13542 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13545 var r = o.records, t = o.totalRecords || r.length;
13547 this.fireEvent("beforeloadadd", this, r, options, o);
13549 if(!options || options.add !== true){
13550 if(this.pruneModifiedRecords){
13551 this.modified = [];
13553 for(var i = 0, len = r.length; i < len; i++){
13557 this.data = this.snapshot;
13558 delete this.snapshot;
13561 this.data.addAll(r);
13562 this.totalLength = t;
13564 this.fireEvent("datachanged", this);
13566 this.totalLength = Math.max(t, this.data.length+r.length);
13570 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13572 var e = new Roo.data.Record({});
13574 e.set(this.parent.displayField, this.parent.emptyTitle);
13575 e.set(this.parent.valueField, '');
13580 this.fireEvent("load", this, r, options, o);
13581 if(options.callback){
13582 options.callback.call(options.scope || this, r, options, true);
13588 * Loads data from a passed data block. A Reader which understands the format of the data
13589 * must have been configured in the constructor.
13590 * @param {Object} data The data block from which to read the Records. The format of the data expected
13591 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13592 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13594 loadData : function(o, append){
13595 var r = this.reader.readRecords(o);
13596 this.loadRecords(r, {add: append}, true);
13600 * using 'cn' the nested child reader read the child array into it's child stores.
13601 * @param {Object} rec The record with a 'children array
13603 loadDataFromChildren : function(rec)
13605 this.loadData(this.reader.toLoadData(rec));
13610 * Gets the number of cached records.
13612 * <em>If using paging, this may not be the total size of the dataset. If the data object
13613 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13614 * the data set size</em>
13616 getCount : function(){
13617 return this.data.length || 0;
13621 * Gets the total number of records in the dataset as returned by the server.
13623 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13624 * the dataset size</em>
13626 getTotalCount : function(){
13627 return this.totalLength || 0;
13631 * Returns the sort state of the Store as an object with two properties:
13633 field {String} The name of the field by which the Records are sorted
13634 direction {String} The sort order, "ASC" or "DESC"
13637 getSortState : function(){
13638 return this.sortInfo;
13642 applySort : function(){
13643 if(this.sortInfo && !this.remoteSort){
13644 var s = this.sortInfo, f = s.field;
13645 var st = this.fields.get(f).sortType;
13646 var fn = function(r1, r2){
13647 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13648 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13650 this.data.sort(s.direction, fn);
13651 if(this.snapshot && this.snapshot != this.data){
13652 this.snapshot.sort(s.direction, fn);
13658 * Sets the default sort column and order to be used by the next load operation.
13659 * @param {String} fieldName The name of the field to sort by.
13660 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13662 setDefaultSort : function(field, dir){
13663 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13667 * Sort the Records.
13668 * If remote sorting is used, the sort is performed on the server, and the cache is
13669 * reloaded. If local sorting is used, the cache is sorted internally.
13670 * @param {String} fieldName The name of the field to sort by.
13671 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13673 sort : function(fieldName, dir){
13674 var f = this.fields.get(fieldName);
13676 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13678 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13679 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13684 this.sortToggle[f.name] = dir;
13685 this.sortInfo = {field: f.name, direction: dir};
13686 if(!this.remoteSort){
13688 this.fireEvent("datachanged", this);
13690 this.load(this.lastOptions);
13695 * Calls the specified function for each of the Records in the cache.
13696 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13697 * Returning <em>false</em> aborts and exits the iteration.
13698 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13700 each : function(fn, scope){
13701 this.data.each(fn, scope);
13705 * Gets all records modified since the last commit. Modified records are persisted across load operations
13706 * (e.g., during paging).
13707 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13709 getModifiedRecords : function(){
13710 return this.modified;
13714 createFilterFn : function(property, value, anyMatch){
13715 if(!value.exec){ // not a regex
13716 value = String(value);
13717 if(value.length == 0){
13720 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13722 return function(r){
13723 return value.test(r.data[property]);
13728 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13729 * @param {String} property A field on your records
13730 * @param {Number} start The record index to start at (defaults to 0)
13731 * @param {Number} end The last record index to include (defaults to length - 1)
13732 * @return {Number} The sum
13734 sum : function(property, start, end){
13735 var rs = this.data.items, v = 0;
13736 start = start || 0;
13737 end = (end || end === 0) ? end : rs.length-1;
13739 for(var i = start; i <= end; i++){
13740 v += (rs[i].data[property] || 0);
13746 * Filter the records by a specified property.
13747 * @param {String} field A field on your records
13748 * @param {String/RegExp} value Either a string that the field
13749 * should start with or a RegExp to test against the field
13750 * @param {Boolean} anyMatch True to match any part not just the beginning
13752 filter : function(property, value, anyMatch){
13753 var fn = this.createFilterFn(property, value, anyMatch);
13754 return fn ? this.filterBy(fn) : this.clearFilter();
13758 * Filter by a function. The specified function will be called with each
13759 * record in this data source. If the function returns true the record is included,
13760 * otherwise it is filtered.
13761 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13762 * @param {Object} scope (optional) The scope of the function (defaults to this)
13764 filterBy : function(fn, scope){
13765 this.snapshot = this.snapshot || this.data;
13766 this.data = this.queryBy(fn, scope||this);
13767 this.fireEvent("datachanged", this);
13771 * Query the records by a specified property.
13772 * @param {String} field A field on your records
13773 * @param {String/RegExp} value Either a string that the field
13774 * should start with or a RegExp to test against the field
13775 * @param {Boolean} anyMatch True to match any part not just the beginning
13776 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13778 query : function(property, value, anyMatch){
13779 var fn = this.createFilterFn(property, value, anyMatch);
13780 return fn ? this.queryBy(fn) : this.data.clone();
13784 * Query by a function. The specified function will be called with each
13785 * record in this data source. If the function returns true the record is included
13787 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13788 * @param {Object} scope (optional) The scope of the function (defaults to this)
13789 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13791 queryBy : function(fn, scope){
13792 var data = this.snapshot || this.data;
13793 return data.filterBy(fn, scope||this);
13797 * Collects unique values for a particular dataIndex from this store.
13798 * @param {String} dataIndex The property to collect
13799 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13800 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13801 * @return {Array} An array of the unique values
13803 collect : function(dataIndex, allowNull, bypassFilter){
13804 var d = (bypassFilter === true && this.snapshot) ?
13805 this.snapshot.items : this.data.items;
13806 var v, sv, r = [], l = {};
13807 for(var i = 0, len = d.length; i < len; i++){
13808 v = d[i].data[dataIndex];
13810 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13819 * Revert to a view of the Record cache with no filtering applied.
13820 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13822 clearFilter : function(suppressEvent){
13823 if(this.snapshot && this.snapshot != this.data){
13824 this.data = this.snapshot;
13825 delete this.snapshot;
13826 if(suppressEvent !== true){
13827 this.fireEvent("datachanged", this);
13833 afterEdit : function(record){
13834 if(this.modified.indexOf(record) == -1){
13835 this.modified.push(record);
13837 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13841 afterReject : function(record){
13842 this.modified.remove(record);
13843 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13847 afterCommit : function(record){
13848 this.modified.remove(record);
13849 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13853 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13854 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13856 commitChanges : function(){
13857 var m = this.modified.slice(0);
13858 this.modified = [];
13859 for(var i = 0, len = m.length; i < len; i++){
13865 * Cancel outstanding changes on all changed records.
13867 rejectChanges : function(){
13868 var m = this.modified.slice(0);
13869 this.modified = [];
13870 for(var i = 0, len = m.length; i < len; i++){
13875 onMetaChange : function(meta, rtype, o){
13876 this.recordType = rtype;
13877 this.fields = rtype.prototype.fields;
13878 delete this.snapshot;
13879 this.sortInfo = meta.sortInfo || this.sortInfo;
13880 this.modified = [];
13881 this.fireEvent('metachange', this, this.reader.meta);
13884 moveIndex : function(data, type)
13886 var index = this.indexOf(data);
13888 var newIndex = index + type;
13892 this.insert(newIndex, data);
13897 * Ext JS Library 1.1.1
13898 * Copyright(c) 2006-2007, Ext JS, LLC.
13900 * Originally Released Under LGPL - original licence link has changed is not relivant.
13903 * <script type="text/javascript">
13907 * @class Roo.data.SimpleStore
13908 * @extends Roo.data.Store
13909 * Small helper class to make creating Stores from Array data easier.
13910 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13911 * @cfg {Array} fields An array of field definition objects, or field name strings.
13912 * @cfg {Object} an existing reader (eg. copied from another store)
13913 * @cfg {Array} data The multi-dimensional array of data
13915 * @param {Object} config
13917 Roo.data.SimpleStore = function(config)
13919 Roo.data.SimpleStore.superclass.constructor.call(this, {
13921 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13924 Roo.data.Record.create(config.fields)
13926 proxy : new Roo.data.MemoryProxy(config.data)
13930 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13932 * Ext JS Library 1.1.1
13933 * Copyright(c) 2006-2007, Ext JS, LLC.
13935 * Originally Released Under LGPL - original licence link has changed is not relivant.
13938 * <script type="text/javascript">
13943 * @extends Roo.data.Store
13944 * @class Roo.data.JsonStore
13945 * Small helper class to make creating Stores for JSON data easier. <br/>
13947 var store = new Roo.data.JsonStore({
13948 url: 'get-images.php',
13950 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13953 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13954 * JsonReader and HttpProxy (unless inline data is provided).</b>
13955 * @cfg {Array} fields An array of field definition objects, or field name strings.
13957 * @param {Object} config
13959 Roo.data.JsonStore = function(c){
13960 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13961 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13962 reader: new Roo.data.JsonReader(c, c.fields)
13965 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13967 * Ext JS Library 1.1.1
13968 * Copyright(c) 2006-2007, Ext JS, LLC.
13970 * Originally Released Under LGPL - original licence link has changed is not relivant.
13973 * <script type="text/javascript">
13977 Roo.data.Field = function(config){
13978 if(typeof config == "string"){
13979 config = {name: config};
13981 Roo.apply(this, config);
13984 this.type = "auto";
13987 var st = Roo.data.SortTypes;
13988 // named sortTypes are supported, here we look them up
13989 if(typeof this.sortType == "string"){
13990 this.sortType = st[this.sortType];
13993 // set default sortType for strings and dates
13994 if(!this.sortType){
13997 this.sortType = st.asUCString;
14000 this.sortType = st.asDate;
14003 this.sortType = st.none;
14008 var stripRe = /[\$,%]/g;
14010 // prebuilt conversion function for this field, instead of
14011 // switching every time we're reading a value
14013 var cv, dateFormat = this.dateFormat;
14018 cv = function(v){ return v; };
14021 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14025 return v !== undefined && v !== null && v !== '' ?
14026 parseInt(String(v).replace(stripRe, ""), 10) : '';
14031 return v !== undefined && v !== null && v !== '' ?
14032 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14037 cv = function(v){ return v === true || v === "true" || v == 1; };
14044 if(v instanceof Date){
14048 if(dateFormat == "timestamp"){
14049 return new Date(v*1000);
14051 return Date.parseDate(v, dateFormat);
14053 var parsed = Date.parse(v);
14054 return parsed ? new Date(parsed) : null;
14063 Roo.data.Field.prototype = {
14071 * Ext JS Library 1.1.1
14072 * Copyright(c) 2006-2007, Ext JS, LLC.
14074 * Originally Released Under LGPL - original licence link has changed is not relivant.
14077 * <script type="text/javascript">
14080 // Base class for reading structured data from a data source. This class is intended to be
14081 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14084 * @class Roo.data.DataReader
14085 * Base class for reading structured data from a data source. This class is intended to be
14086 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14089 Roo.data.DataReader = function(meta, recordType){
14093 this.recordType = recordType instanceof Array ?
14094 Roo.data.Record.create(recordType) : recordType;
14097 Roo.data.DataReader.prototype = {
14100 readerType : 'Data',
14102 * Create an empty record
14103 * @param {Object} data (optional) - overlay some values
14104 * @return {Roo.data.Record} record created.
14106 newRow : function(d) {
14108 this.recordType.prototype.fields.each(function(c) {
14110 case 'int' : da[c.name] = 0; break;
14111 case 'date' : da[c.name] = new Date(); break;
14112 case 'float' : da[c.name] = 0.0; break;
14113 case 'boolean' : da[c.name] = false; break;
14114 default : da[c.name] = ""; break;
14118 return new this.recordType(Roo.apply(da, d));
14124 * Ext JS Library 1.1.1
14125 * Copyright(c) 2006-2007, Ext JS, LLC.
14127 * Originally Released Under LGPL - original licence link has changed is not relivant.
14130 * <script type="text/javascript">
14134 * @class Roo.data.DataProxy
14135 * @extends Roo.data.Observable
14136 * This class is an abstract base class for implementations which provide retrieval of
14137 * unformatted data objects.<br>
14139 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14140 * (of the appropriate type which knows how to parse the data object) to provide a block of
14141 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14143 * Custom implementations must implement the load method as described in
14144 * {@link Roo.data.HttpProxy#load}.
14146 Roo.data.DataProxy = function(){
14149 * @event beforeload
14150 * Fires before a network request is made to retrieve a data object.
14151 * @param {Object} This DataProxy object.
14152 * @param {Object} params The params parameter to the load function.
14157 * Fires before the load method's callback is called.
14158 * @param {Object} This DataProxy object.
14159 * @param {Object} o The data object.
14160 * @param {Object} arg The callback argument object passed to the load function.
14164 * @event loadexception
14165 * Fires if an Exception occurs during data retrieval.
14166 * @param {Object} This DataProxy object.
14167 * @param {Object} o The data object.
14168 * @param {Object} arg The callback argument object passed to the load function.
14169 * @param {Object} e The Exception.
14171 loadexception : true
14173 Roo.data.DataProxy.superclass.constructor.call(this);
14176 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14179 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14183 * Ext JS Library 1.1.1
14184 * Copyright(c) 2006-2007, Ext JS, LLC.
14186 * Originally Released Under LGPL - original licence link has changed is not relivant.
14189 * <script type="text/javascript">
14192 * @class Roo.data.MemoryProxy
14193 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14194 * to the Reader when its load method is called.
14196 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14198 Roo.data.MemoryProxy = function(data){
14202 Roo.data.MemoryProxy.superclass.constructor.call(this);
14206 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14209 * Load data from the requested source (in this case an in-memory
14210 * data object passed to the constructor), read the data object into
14211 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14212 * process that block using the passed callback.
14213 * @param {Object} params This parameter is not used by the MemoryProxy class.
14214 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14215 * object into a block of Roo.data.Records.
14216 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14217 * The function must be passed <ul>
14218 * <li>The Record block object</li>
14219 * <li>The "arg" argument from the load function</li>
14220 * <li>A boolean success indicator</li>
14222 * @param {Object} scope The scope in which to call the callback
14223 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14225 load : function(params, reader, callback, scope, arg){
14226 params = params || {};
14229 result = reader.readRecords(params.data ? params.data :this.data);
14231 this.fireEvent("loadexception", this, arg, null, e);
14232 callback.call(scope, null, arg, false);
14235 callback.call(scope, result, arg, true);
14239 update : function(params, records){
14244 * Ext JS Library 1.1.1
14245 * Copyright(c) 2006-2007, Ext JS, LLC.
14247 * Originally Released Under LGPL - original licence link has changed is not relivant.
14250 * <script type="text/javascript">
14253 * @class Roo.data.HttpProxy
14254 * @extends Roo.data.DataProxy
14255 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14256 * configured to reference a certain URL.<br><br>
14258 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14259 * from which the running page was served.<br><br>
14261 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14263 * Be aware that to enable the browser to parse an XML document, the server must set
14264 * the Content-Type header in the HTTP response to "text/xml".
14266 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14267 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14268 * will be used to make the request.
14270 Roo.data.HttpProxy = function(conn){
14271 Roo.data.HttpProxy.superclass.constructor.call(this);
14272 // is conn a conn config or a real conn?
14274 this.useAjax = !conn || !conn.events;
14278 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14279 // thse are take from connection...
14282 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14285 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14286 * extra parameters to each request made by this object. (defaults to undefined)
14289 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14290 * to each request made by this object. (defaults to undefined)
14293 * @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)
14296 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14299 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14305 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14309 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14310 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14311 * a finer-grained basis than the DataProxy events.
14313 getConnection : function(){
14314 return this.useAjax ? Roo.Ajax : this.conn;
14318 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14319 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14320 * process that block using the passed callback.
14321 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14322 * for the request to the remote server.
14323 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14324 * object into a block of Roo.data.Records.
14325 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14326 * The function must be passed <ul>
14327 * <li>The Record block object</li>
14328 * <li>The "arg" argument from the load function</li>
14329 * <li>A boolean success indicator</li>
14331 * @param {Object} scope The scope in which to call the callback
14332 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14334 load : function(params, reader, callback, scope, arg){
14335 if(this.fireEvent("beforeload", this, params) !== false){
14337 params : params || {},
14339 callback : callback,
14344 callback : this.loadResponse,
14348 Roo.applyIf(o, this.conn);
14349 if(this.activeRequest){
14350 Roo.Ajax.abort(this.activeRequest);
14352 this.activeRequest = Roo.Ajax.request(o);
14354 this.conn.request(o);
14357 callback.call(scope||this, null, arg, false);
14362 loadResponse : function(o, success, response){
14363 delete this.activeRequest;
14365 this.fireEvent("loadexception", this, o, response);
14366 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14371 result = o.reader.read(response);
14373 this.fireEvent("loadexception", this, o, response, e);
14374 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14378 this.fireEvent("load", this, o, o.request.arg);
14379 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14383 update : function(dataSet){
14388 updateResponse : function(dataSet){
14393 * Ext JS Library 1.1.1
14394 * Copyright(c) 2006-2007, Ext JS, LLC.
14396 * Originally Released Under LGPL - original licence link has changed is not relivant.
14399 * <script type="text/javascript">
14403 * @class Roo.data.ScriptTagProxy
14404 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14405 * other than the originating domain of the running page.<br><br>
14407 * <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
14408 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14410 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14411 * source code that is used as the source inside a <script> tag.<br><br>
14413 * In order for the browser to process the returned data, the server must wrap the data object
14414 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14415 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14416 * depending on whether the callback name was passed:
14419 boolean scriptTag = false;
14420 String cb = request.getParameter("callback");
14423 response.setContentType("text/javascript");
14425 response.setContentType("application/x-json");
14427 Writer out = response.getWriter();
14429 out.write(cb + "(");
14431 out.print(dataBlock.toJsonString());
14438 * @param {Object} config A configuration object.
14440 Roo.data.ScriptTagProxy = function(config){
14441 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14442 Roo.apply(this, config);
14443 this.head = document.getElementsByTagName("head")[0];
14446 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14448 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14450 * @cfg {String} url The URL from which to request the data object.
14453 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14457 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14458 * the server the name of the callback function set up by the load call to process the returned data object.
14459 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14460 * javascript output which calls this named function passing the data object as its only parameter.
14462 callbackParam : "callback",
14464 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14465 * name to the request.
14470 * Load data from the configured URL, read the data object into
14471 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14472 * process that block using the passed callback.
14473 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14474 * for the request to the remote server.
14475 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14476 * object into a block of Roo.data.Records.
14477 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14478 * The function must be passed <ul>
14479 * <li>The Record block object</li>
14480 * <li>The "arg" argument from the load function</li>
14481 * <li>A boolean success indicator</li>
14483 * @param {Object} scope The scope in which to call the callback
14484 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14486 load : function(params, reader, callback, scope, arg){
14487 if(this.fireEvent("beforeload", this, params) !== false){
14489 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14491 var url = this.url;
14492 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14494 url += "&_dc=" + (new Date().getTime());
14496 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14499 cb : "stcCallback"+transId,
14500 scriptId : "stcScript"+transId,
14504 callback : callback,
14510 window[trans.cb] = function(o){
14511 conn.handleResponse(o, trans);
14514 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14516 if(this.autoAbort !== false){
14520 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14522 var script = document.createElement("script");
14523 script.setAttribute("src", url);
14524 script.setAttribute("type", "text/javascript");
14525 script.setAttribute("id", trans.scriptId);
14526 this.head.appendChild(script);
14528 this.trans = trans;
14530 callback.call(scope||this, null, arg, false);
14535 isLoading : function(){
14536 return this.trans ? true : false;
14540 * Abort the current server request.
14542 abort : function(){
14543 if(this.isLoading()){
14544 this.destroyTrans(this.trans);
14549 destroyTrans : function(trans, isLoaded){
14550 this.head.removeChild(document.getElementById(trans.scriptId));
14551 clearTimeout(trans.timeoutId);
14553 window[trans.cb] = undefined;
14555 delete window[trans.cb];
14558 // if hasn't been loaded, wait for load to remove it to prevent script error
14559 window[trans.cb] = function(){
14560 window[trans.cb] = undefined;
14562 delete window[trans.cb];
14569 handleResponse : function(o, trans){
14570 this.trans = false;
14571 this.destroyTrans(trans, true);
14574 result = trans.reader.readRecords(o);
14576 this.fireEvent("loadexception", this, o, trans.arg, e);
14577 trans.callback.call(trans.scope||window, null, trans.arg, false);
14580 this.fireEvent("load", this, o, trans.arg);
14581 trans.callback.call(trans.scope||window, result, trans.arg, true);
14585 handleFailure : function(trans){
14586 this.trans = false;
14587 this.destroyTrans(trans, false);
14588 this.fireEvent("loadexception", this, null, trans.arg);
14589 trans.callback.call(trans.scope||window, null, trans.arg, false);
14593 * Ext JS Library 1.1.1
14594 * Copyright(c) 2006-2007, Ext JS, LLC.
14596 * Originally Released Under LGPL - original licence link has changed is not relivant.
14599 * <script type="text/javascript">
14603 * @class Roo.data.JsonReader
14604 * @extends Roo.data.DataReader
14605 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14606 * based on mappings in a provided Roo.data.Record constructor.
14608 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14609 * in the reply previously.
14614 var RecordDef = Roo.data.Record.create([
14615 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14616 {name: 'occupation'} // This field will use "occupation" as the mapping.
14618 var myReader = new Roo.data.JsonReader({
14619 totalProperty: "results", // The property which contains the total dataset size (optional)
14620 root: "rows", // The property which contains an Array of row objects
14621 id: "id" // The property within each row object that provides an ID for the record (optional)
14625 * This would consume a JSON file like this:
14627 { 'results': 2, 'rows': [
14628 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14629 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14632 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14633 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14634 * paged from the remote server.
14635 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14636 * @cfg {String} root name of the property which contains the Array of row objects.
14637 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14638 * @cfg {Array} fields Array of field definition objects
14640 * Create a new JsonReader
14641 * @param {Object} meta Metadata configuration options
14642 * @param {Object} recordType Either an Array of field definition objects,
14643 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14645 Roo.data.JsonReader = function(meta, recordType){
14648 // set some defaults:
14649 Roo.applyIf(meta, {
14650 totalProperty: 'total',
14651 successProperty : 'success',
14656 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14658 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14660 readerType : 'Json',
14663 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14664 * Used by Store query builder to append _requestMeta to params.
14667 metaFromRemote : false,
14669 * This method is only used by a DataProxy which has retrieved data from a remote server.
14670 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14671 * @return {Object} data A data block which is used by an Roo.data.Store object as
14672 * a cache of Roo.data.Records.
14674 read : function(response){
14675 var json = response.responseText;
14677 var o = /* eval:var:o */ eval("("+json+")");
14679 throw {message: "JsonReader.read: Json object not found"};
14685 this.metaFromRemote = true;
14686 this.meta = o.metaData;
14687 this.recordType = Roo.data.Record.create(o.metaData.fields);
14688 this.onMetaChange(this.meta, this.recordType, o);
14690 return this.readRecords(o);
14693 // private function a store will implement
14694 onMetaChange : function(meta, recordType, o){
14701 simpleAccess: function(obj, subsc) {
14708 getJsonAccessor: function(){
14710 return function(expr) {
14712 return(re.test(expr))
14713 ? new Function("obj", "return obj." + expr)
14718 return Roo.emptyFn;
14723 * Create a data block containing Roo.data.Records from an XML document.
14724 * @param {Object} o An object which contains an Array of row objects in the property specified
14725 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14726 * which contains the total size of the dataset.
14727 * @return {Object} data A data block which is used by an Roo.data.Store object as
14728 * a cache of Roo.data.Records.
14730 readRecords : function(o){
14732 * After any data loads, the raw JSON data is available for further custom processing.
14736 var s = this.meta, Record = this.recordType,
14737 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14739 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14741 if(s.totalProperty) {
14742 this.getTotal = this.getJsonAccessor(s.totalProperty);
14744 if(s.successProperty) {
14745 this.getSuccess = this.getJsonAccessor(s.successProperty);
14747 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14749 var g = this.getJsonAccessor(s.id);
14750 this.getId = function(rec) {
14752 return (r === undefined || r === "") ? null : r;
14755 this.getId = function(){return null;};
14758 for(var jj = 0; jj < fl; jj++){
14760 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14761 this.ef[jj] = this.getJsonAccessor(map);
14765 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14766 if(s.totalProperty){
14767 var vt = parseInt(this.getTotal(o), 10);
14772 if(s.successProperty){
14773 var vs = this.getSuccess(o);
14774 if(vs === false || vs === 'false'){
14779 for(var i = 0; i < c; i++){
14782 var id = this.getId(n);
14783 for(var j = 0; j < fl; j++){
14785 var v = this.ef[j](n);
14787 Roo.log('missing convert for ' + f.name);
14791 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14793 var record = new Record(values, id);
14795 records[i] = record;
14801 totalRecords : totalRecords
14804 // used when loading children.. @see loadDataFromChildren
14805 toLoadData: function(rec)
14807 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14808 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14809 return { data : data, total : data.length };
14814 * Ext JS Library 1.1.1
14815 * Copyright(c) 2006-2007, Ext JS, LLC.
14817 * Originally Released Under LGPL - original licence link has changed is not relivant.
14820 * <script type="text/javascript">
14824 * @class Roo.data.ArrayReader
14825 * @extends Roo.data.DataReader
14826 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14827 * Each element of that Array represents a row of data fields. The
14828 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14829 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14833 var RecordDef = Roo.data.Record.create([
14834 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14835 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14837 var myReader = new Roo.data.ArrayReader({
14838 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14842 * This would consume an Array like this:
14844 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14848 * Create a new JsonReader
14849 * @param {Object} meta Metadata configuration options.
14850 * @param {Object|Array} recordType Either an Array of field definition objects
14852 * @cfg {Array} fields Array of field definition objects
14853 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14854 * as specified to {@link Roo.data.Record#create},
14855 * or an {@link Roo.data.Record} object
14858 * created using {@link Roo.data.Record#create}.
14860 Roo.data.ArrayReader = function(meta, recordType)
14862 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14865 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14868 * Create a data block containing Roo.data.Records from an XML document.
14869 * @param {Object} o An Array of row objects which represents the dataset.
14870 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14871 * a cache of Roo.data.Records.
14873 readRecords : function(o)
14875 var sid = this.meta ? this.meta.id : null;
14876 var recordType = this.recordType, fields = recordType.prototype.fields;
14879 for(var i = 0; i < root.length; i++){
14882 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14883 for(var j = 0, jlen = fields.length; j < jlen; j++){
14884 var f = fields.items[j];
14885 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14886 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14888 values[f.name] = v;
14890 var record = new recordType(values, id);
14892 records[records.length] = record;
14896 totalRecords : records.length
14899 // used when loading children.. @see loadDataFromChildren
14900 toLoadData: function(rec)
14902 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14903 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14914 * @class Roo.bootstrap.ComboBox
14915 * @extends Roo.bootstrap.TriggerField
14916 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14917 * @cfg {Boolean} append (true|false) default false
14918 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14919 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14920 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14921 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14922 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14923 * @cfg {Boolean} animate default true
14924 * @cfg {Boolean} emptyResultText only for touch device
14925 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14926 * @cfg {String} emptyTitle default ''
14927 * @cfg {Number} width fixed with? experimental
14929 * Create a new ComboBox.
14930 * @param {Object} config Configuration options
14932 Roo.bootstrap.ComboBox = function(config){
14933 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14937 * Fires when the dropdown list is expanded
14938 * @param {Roo.bootstrap.ComboBox} combo This combo box
14943 * Fires when the dropdown list is collapsed
14944 * @param {Roo.bootstrap.ComboBox} combo This combo box
14948 * @event beforeselect
14949 * Fires before a list item is selected. Return false to cancel the selection.
14950 * @param {Roo.bootstrap.ComboBox} combo This combo box
14951 * @param {Roo.data.Record} record The data record returned from the underlying store
14952 * @param {Number} index The index of the selected item in the dropdown list
14954 'beforeselect' : true,
14957 * Fires when a list item is selected
14958 * @param {Roo.bootstrap.ComboBox} combo This combo box
14959 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14960 * @param {Number} index The index of the selected item in the dropdown list
14964 * @event beforequery
14965 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14966 * The event object passed has these properties:
14967 * @param {Roo.bootstrap.ComboBox} combo This combo box
14968 * @param {String} query The query
14969 * @param {Boolean} forceAll true to force "all" query
14970 * @param {Boolean} cancel true to cancel the query
14971 * @param {Object} e The query event object
14973 'beforequery': true,
14976 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14977 * @param {Roo.bootstrap.ComboBox} combo This combo box
14982 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14983 * @param {Roo.bootstrap.ComboBox} combo This combo box
14984 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14989 * Fires when the remove value from the combobox array
14990 * @param {Roo.bootstrap.ComboBox} combo This combo box
14994 * @event afterremove
14995 * Fires when the remove value from the combobox array
14996 * @param {Roo.bootstrap.ComboBox} combo This combo box
14998 'afterremove' : true,
15000 * @event specialfilter
15001 * Fires when specialfilter
15002 * @param {Roo.bootstrap.ComboBox} combo This combo box
15004 'specialfilter' : true,
15007 * Fires when tick the element
15008 * @param {Roo.bootstrap.ComboBox} combo This combo box
15012 * @event touchviewdisplay
15013 * Fires when touch view require special display (default is using displayField)
15014 * @param {Roo.bootstrap.ComboBox} combo This combo box
15015 * @param {Object} cfg set html .
15017 'touchviewdisplay' : true
15022 this.tickItems = [];
15024 this.selectedIndex = -1;
15025 if(this.mode == 'local'){
15026 if(config.queryDelay === undefined){
15027 this.queryDelay = 10;
15029 if(config.minChars === undefined){
15035 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15038 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15039 * rendering into an Roo.Editor, defaults to false)
15042 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15043 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15046 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15049 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15050 * the dropdown list (defaults to undefined, with no header element)
15054 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15058 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15060 listWidth: undefined,
15062 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15063 * mode = 'remote' or 'text' if mode = 'local')
15065 displayField: undefined,
15068 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15069 * mode = 'remote' or 'value' if mode = 'local').
15070 * Note: use of a valueField requires the user make a selection
15071 * in order for a value to be mapped.
15073 valueField: undefined,
15075 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15080 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15081 * field's data value (defaults to the underlying DOM element's name)
15083 hiddenName: undefined,
15085 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15089 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15091 selectedClass: 'active',
15094 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15098 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15099 * anchor positions (defaults to 'tl-bl')
15101 listAlign: 'tl-bl?',
15103 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15107 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15108 * query specified by the allQuery config option (defaults to 'query')
15110 triggerAction: 'query',
15112 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15113 * (defaults to 4, does not apply if editable = false)
15117 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15118 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15122 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15123 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15127 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15128 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15132 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15133 * when editable = true (defaults to false)
15135 selectOnFocus:false,
15137 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15139 queryParam: 'query',
15141 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15142 * when mode = 'remote' (defaults to 'Loading...')
15144 loadingText: 'Loading...',
15146 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15150 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15154 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15155 * traditional select (defaults to true)
15159 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15163 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15167 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15168 * listWidth has a higher value)
15172 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15173 * allow the user to set arbitrary text into the field (defaults to false)
15175 forceSelection:false,
15177 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15178 * if typeAhead = true (defaults to 250)
15180 typeAheadDelay : 250,
15182 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15183 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15185 valueNotFoundText : undefined,
15187 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15189 blockFocus : false,
15192 * @cfg {Boolean} disableClear Disable showing of clear button.
15194 disableClear : false,
15196 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15198 alwaysQuery : false,
15201 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15206 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15208 invalidClass : "has-warning",
15211 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15213 validClass : "has-success",
15216 * @cfg {Boolean} specialFilter (true|false) special filter default false
15218 specialFilter : false,
15221 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15223 mobileTouchView : true,
15226 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15228 useNativeIOS : false,
15231 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15233 mobile_restrict_height : false,
15235 ios_options : false,
15247 btnPosition : 'right',
15248 triggerList : true,
15249 showToggleBtn : true,
15251 emptyResultText: 'Empty',
15252 triggerText : 'Select',
15256 // element that contains real text value.. (when hidden is used..)
15258 getAutoCreate : function()
15263 * Render classic select for iso
15266 if(Roo.isIOS && this.useNativeIOS){
15267 cfg = this.getAutoCreateNativeIOS();
15275 if(Roo.isTouch && this.mobileTouchView){
15276 cfg = this.getAutoCreateTouchView();
15283 if(!this.tickable){
15284 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15289 * ComboBox with tickable selections
15292 var align = this.labelAlign || this.parentLabelAlign();
15295 cls : 'form-group roo-combobox-tickable' //input-group
15298 var btn_text_select = '';
15299 var btn_text_done = '';
15300 var btn_text_cancel = '';
15302 if (this.btn_text_show) {
15303 btn_text_select = 'Select';
15304 btn_text_done = 'Done';
15305 btn_text_cancel = 'Cancel';
15310 cls : 'tickable-buttons',
15315 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15316 //html : this.triggerText
15317 html: btn_text_select
15323 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15325 html: btn_text_done
15331 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15333 html: btn_text_cancel
15339 buttons.cn.unshift({
15341 cls: 'roo-select2-search-field-input'
15347 Roo.each(buttons.cn, function(c){
15349 c.cls += ' btn-' + _this.size;
15352 if (_this.disabled) {
15359 style : 'display: contents',
15364 cls: 'form-hidden-field'
15368 cls: 'roo-select2-choices',
15372 cls: 'roo-select2-search-field',
15383 cls: 'roo-select2-container input-group roo-select2-container-multi',
15389 // cls: 'typeahead typeahead-long dropdown-menu',
15390 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15395 if(this.hasFeedback && !this.allowBlank){
15399 cls: 'glyphicon form-control-feedback'
15402 combobox.cn.push(feedback);
15409 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15410 tooltip : 'This field is required'
15412 if (Roo.bootstrap.version == 4) {
15415 style : 'display:none'
15418 if (align ==='left' && this.fieldLabel.length) {
15420 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15427 cls : 'control-label col-form-label',
15428 html : this.fieldLabel
15440 var labelCfg = cfg.cn[1];
15441 var contentCfg = cfg.cn[2];
15444 if(this.indicatorpos == 'right'){
15450 cls : 'control-label col-form-label',
15454 html : this.fieldLabel
15470 labelCfg = cfg.cn[0];
15471 contentCfg = cfg.cn[1];
15475 if(this.labelWidth > 12){
15476 labelCfg.style = "width: " + this.labelWidth + 'px';
15478 if(this.width * 1 > 0){
15479 contentCfg.style = "width: " + this.width + 'px';
15481 if(this.labelWidth < 13 && this.labelmd == 0){
15482 this.labelmd = this.labelWidth;
15485 if(this.labellg > 0){
15486 labelCfg.cls += ' col-lg-' + this.labellg;
15487 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15490 if(this.labelmd > 0){
15491 labelCfg.cls += ' col-md-' + this.labelmd;
15492 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15495 if(this.labelsm > 0){
15496 labelCfg.cls += ' col-sm-' + this.labelsm;
15497 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15500 if(this.labelxs > 0){
15501 labelCfg.cls += ' col-xs-' + this.labelxs;
15502 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15506 } else if ( this.fieldLabel.length) {
15507 // Roo.log(" label");
15512 //cls : 'input-group-addon',
15513 html : this.fieldLabel
15518 if(this.indicatorpos == 'right'){
15522 //cls : 'input-group-addon',
15523 html : this.fieldLabel
15533 // Roo.log(" no label && no align");
15540 ['xs','sm','md','lg'].map(function(size){
15541 if (settings[size]) {
15542 cfg.cls += ' col-' + size + '-' + settings[size];
15550 _initEventsCalled : false,
15553 initEvents: function()
15555 if (this._initEventsCalled) { // as we call render... prevent looping...
15558 this._initEventsCalled = true;
15561 throw "can not find store for combo";
15564 this.indicator = this.indicatorEl();
15566 this.store = Roo.factory(this.store, Roo.data);
15567 this.store.parent = this;
15569 // if we are building from html. then this element is so complex, that we can not really
15570 // use the rendered HTML.
15571 // so we have to trash and replace the previous code.
15572 if (Roo.XComponent.build_from_html) {
15573 // remove this element....
15574 var e = this.el.dom, k=0;
15575 while (e ) { e = e.previousSibling; ++k;}
15580 this.rendered = false;
15582 this.render(this.parent().getChildContainer(true), k);
15585 if(Roo.isIOS && this.useNativeIOS){
15586 this.initIOSView();
15594 if(Roo.isTouch && this.mobileTouchView){
15595 this.initTouchView();
15600 this.initTickableEvents();
15604 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15606 if(this.hiddenName){
15608 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15610 this.hiddenField.dom.value =
15611 this.hiddenValue !== undefined ? this.hiddenValue :
15612 this.value !== undefined ? this.value : '';
15614 // prevent input submission
15615 this.el.dom.removeAttribute('name');
15616 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15621 // this.el.dom.setAttribute('autocomplete', 'off');
15624 var cls = 'x-combo-list';
15626 //this.list = new Roo.Layer({
15627 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15633 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15634 _this.list.setWidth(lw);
15637 this.list.on('mouseover', this.onViewOver, this);
15638 this.list.on('mousemove', this.onViewMove, this);
15639 this.list.on('scroll', this.onViewScroll, this);
15642 this.list.swallowEvent('mousewheel');
15643 this.assetHeight = 0;
15646 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15647 this.assetHeight += this.header.getHeight();
15650 this.innerList = this.list.createChild({cls:cls+'-inner'});
15651 this.innerList.on('mouseover', this.onViewOver, this);
15652 this.innerList.on('mousemove', this.onViewMove, this);
15653 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15655 if(this.allowBlank && !this.pageSize && !this.disableClear){
15656 this.footer = this.list.createChild({cls:cls+'-ft'});
15657 this.pageTb = new Roo.Toolbar(this.footer);
15661 this.footer = this.list.createChild({cls:cls+'-ft'});
15662 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15663 {pageSize: this.pageSize});
15667 if (this.pageTb && this.allowBlank && !this.disableClear) {
15669 this.pageTb.add(new Roo.Toolbar.Fill(), {
15670 cls: 'x-btn-icon x-btn-clear',
15672 handler: function()
15675 _this.clearValue();
15676 _this.onSelect(false, -1);
15681 this.assetHeight += this.footer.getHeight();
15686 this.tpl = Roo.bootstrap.version == 4 ?
15687 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15688 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15691 this.view = new Roo.View(this.list, this.tpl, {
15692 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15694 //this.view.wrapEl.setDisplayed(false);
15695 this.view.on('click', this.onViewClick, this);
15698 this.store.on('beforeload', this.onBeforeLoad, this);
15699 this.store.on('load', this.onLoad, this);
15700 this.store.on('loadexception', this.onLoadException, this);
15702 if(this.resizable){
15703 this.resizer = new Roo.Resizable(this.list, {
15704 pinned:true, handles:'se'
15706 this.resizer.on('resize', function(r, w, h){
15707 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15708 this.listWidth = w;
15709 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15710 this.restrictHeight();
15712 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15715 if(!this.editable){
15716 this.editable = true;
15717 this.setEditable(false);
15722 if (typeof(this.events.add.listeners) != 'undefined') {
15724 this.addicon = this.wrap.createChild(
15725 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15727 this.addicon.on('click', function(e) {
15728 this.fireEvent('add', this);
15731 if (typeof(this.events.edit.listeners) != 'undefined') {
15733 this.editicon = this.wrap.createChild(
15734 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15735 if (this.addicon) {
15736 this.editicon.setStyle('margin-left', '40px');
15738 this.editicon.on('click', function(e) {
15740 // we fire even if inothing is selected..
15741 this.fireEvent('edit', this, this.lastData );
15747 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15748 "up" : function(e){
15749 this.inKeyMode = true;
15753 "down" : function(e){
15754 if(!this.isExpanded()){
15755 this.onTriggerClick();
15757 this.inKeyMode = true;
15762 "enter" : function(e){
15763 // this.onViewClick();
15767 if(this.fireEvent("specialkey", this, e)){
15768 this.onViewClick(false);
15774 "esc" : function(e){
15778 "tab" : function(e){
15781 if(this.fireEvent("specialkey", this, e)){
15782 this.onViewClick(false);
15790 doRelay : function(foo, bar, hname){
15791 if(hname == 'down' || this.scope.isExpanded()){
15792 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15801 this.queryDelay = Math.max(this.queryDelay || 10,
15802 this.mode == 'local' ? 10 : 250);
15805 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15807 if(this.typeAhead){
15808 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15810 if(this.editable !== false){
15811 this.inputEl().on("keyup", this.onKeyUp, this);
15813 if(this.forceSelection){
15814 this.inputEl().on('blur', this.doForce, this);
15818 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15819 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15823 initTickableEvents: function()
15827 if(this.hiddenName){
15829 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15831 this.hiddenField.dom.value =
15832 this.hiddenValue !== undefined ? this.hiddenValue :
15833 this.value !== undefined ? this.value : '';
15835 // prevent input submission
15836 this.el.dom.removeAttribute('name');
15837 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15842 // this.list = this.el.select('ul.dropdown-menu',true).first();
15844 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15845 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15846 if(this.triggerList){
15847 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15850 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15851 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15853 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15854 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15856 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15857 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15859 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15860 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15861 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15864 this.cancelBtn.hide();
15869 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15870 _this.list.setWidth(lw);
15873 this.list.on('mouseover', this.onViewOver, this);
15874 this.list.on('mousemove', this.onViewMove, this);
15876 this.list.on('scroll', this.onViewScroll, this);
15879 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15880 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15883 this.view = new Roo.View(this.list, this.tpl, {
15888 selectedClass: this.selectedClass
15891 //this.view.wrapEl.setDisplayed(false);
15892 this.view.on('click', this.onViewClick, this);
15896 this.store.on('beforeload', this.onBeforeLoad, this);
15897 this.store.on('load', this.onLoad, this);
15898 this.store.on('loadexception', this.onLoadException, this);
15901 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15902 "up" : function(e){
15903 this.inKeyMode = true;
15907 "down" : function(e){
15908 this.inKeyMode = true;
15912 "enter" : function(e){
15913 if(this.fireEvent("specialkey", this, e)){
15914 this.onViewClick(false);
15920 "esc" : function(e){
15921 this.onTickableFooterButtonClick(e, false, false);
15924 "tab" : function(e){
15925 this.fireEvent("specialkey", this, e);
15927 this.onTickableFooterButtonClick(e, false, false);
15934 doRelay : function(e, fn, key){
15935 if(this.scope.isExpanded()){
15936 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15945 this.queryDelay = Math.max(this.queryDelay || 10,
15946 this.mode == 'local' ? 10 : 250);
15949 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15951 if(this.typeAhead){
15952 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15955 if(this.editable !== false){
15956 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15959 this.indicator = this.indicatorEl();
15961 if(this.indicator){
15962 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15963 this.indicator.hide();
15968 onDestroy : function(){
15970 this.view.setStore(null);
15971 this.view.el.removeAllListeners();
15972 this.view.el.remove();
15973 this.view.purgeListeners();
15976 this.list.dom.innerHTML = '';
15980 this.store.un('beforeload', this.onBeforeLoad, this);
15981 this.store.un('load', this.onLoad, this);
15982 this.store.un('loadexception', this.onLoadException, this);
15984 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15988 fireKey : function(e){
15989 if(e.isNavKeyPress() && !this.list.isVisible()){
15990 this.fireEvent("specialkey", this, e);
15995 onResize: function(w, h)
15999 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16001 // if(typeof w != 'number'){
16002 // // we do not handle it!?!?
16005 // var tw = this.trigger.getWidth();
16006 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16007 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16009 // this.inputEl().setWidth( this.adjustWidth('input', x));
16011 // //this.trigger.setStyle('left', x+'px');
16013 // if(this.list && this.listWidth === undefined){
16014 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16015 // this.list.setWidth(lw);
16016 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16024 * Allow or prevent the user from directly editing the field text. If false is passed,
16025 * the user will only be able to select from the items defined in the dropdown list. This method
16026 * is the runtime equivalent of setting the 'editable' config option at config time.
16027 * @param {Boolean} value True to allow the user to directly edit the field text
16029 setEditable : function(value){
16030 if(value == this.editable){
16033 this.editable = value;
16035 this.inputEl().dom.setAttribute('readOnly', true);
16036 this.inputEl().on('mousedown', this.onTriggerClick, this);
16037 this.inputEl().addClass('x-combo-noedit');
16039 this.inputEl().dom.setAttribute('readOnly', false);
16040 this.inputEl().un('mousedown', this.onTriggerClick, this);
16041 this.inputEl().removeClass('x-combo-noedit');
16047 onBeforeLoad : function(combo,opts){
16048 if(!this.hasFocus){
16052 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16054 this.restrictHeight();
16055 this.selectedIndex = -1;
16059 onLoad : function(){
16061 this.hasQuery = false;
16063 if(!this.hasFocus){
16067 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16068 this.loading.hide();
16071 if(this.store.getCount() > 0){
16074 this.restrictHeight();
16075 if(this.lastQuery == this.allQuery){
16076 if(this.editable && !this.tickable){
16077 this.inputEl().dom.select();
16081 !this.selectByValue(this.value, true) &&
16084 !this.store.lastOptions ||
16085 typeof(this.store.lastOptions.add) == 'undefined' ||
16086 this.store.lastOptions.add != true
16089 this.select(0, true);
16092 if(this.autoFocus){
16095 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16096 this.taTask.delay(this.typeAheadDelay);
16100 this.onEmptyResults();
16106 onLoadException : function()
16108 this.hasQuery = false;
16110 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16111 this.loading.hide();
16114 if(this.tickable && this.editable){
16119 // only causes errors at present
16120 //Roo.log(this.store.reader.jsonData);
16121 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16123 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16129 onTypeAhead : function(){
16130 if(this.store.getCount() > 0){
16131 var r = this.store.getAt(0);
16132 var newValue = r.data[this.displayField];
16133 var len = newValue.length;
16134 var selStart = this.getRawValue().length;
16136 if(selStart != len){
16137 this.setRawValue(newValue);
16138 this.selectText(selStart, newValue.length);
16144 onSelect : function(record, index){
16146 if(this.fireEvent('beforeselect', this, record, index) !== false){
16148 this.setFromData(index > -1 ? record.data : false);
16151 this.fireEvent('select', this, record, index);
16156 * Returns the currently selected field value or empty string if no value is set.
16157 * @return {String} value The selected value
16159 getValue : function()
16161 if(Roo.isIOS && this.useNativeIOS){
16162 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16166 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16169 if(this.valueField){
16170 return typeof this.value != 'undefined' ? this.value : '';
16172 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16176 getRawValue : function()
16178 if(Roo.isIOS && this.useNativeIOS){
16179 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16182 var v = this.inputEl().getValue();
16188 * Clears any text/value currently set in the field
16190 clearValue : function(){
16192 if(this.hiddenField){
16193 this.hiddenField.dom.value = '';
16196 this.setRawValue('');
16197 this.lastSelectionText = '';
16198 this.lastData = false;
16200 var close = this.closeTriggerEl();
16211 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16212 * will be displayed in the field. If the value does not match the data value of an existing item,
16213 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16214 * Otherwise the field will be blank (although the value will still be set).
16215 * @param {String} value The value to match
16217 setValue : function(v)
16219 if(Roo.isIOS && this.useNativeIOS){
16220 this.setIOSValue(v);
16230 if(this.valueField){
16231 var r = this.findRecord(this.valueField, v);
16233 text = r.data[this.displayField];
16234 }else if(this.valueNotFoundText !== undefined){
16235 text = this.valueNotFoundText;
16238 this.lastSelectionText = text;
16239 if(this.hiddenField){
16240 this.hiddenField.dom.value = v;
16242 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16245 var close = this.closeTriggerEl();
16248 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16254 * @property {Object} the last set data for the element
16259 * Sets the value of the field based on a object which is related to the record format for the store.
16260 * @param {Object} value the value to set as. or false on reset?
16262 setFromData : function(o){
16269 var dv = ''; // display value
16270 var vv = ''; // value value..
16272 if (this.displayField) {
16273 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16275 // this is an error condition!!!
16276 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16279 if(this.valueField){
16280 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16283 var close = this.closeTriggerEl();
16286 if(dv.length || vv * 1 > 0){
16288 this.blockFocus=true;
16294 if(this.hiddenField){
16295 this.hiddenField.dom.value = vv;
16297 this.lastSelectionText = dv;
16298 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16302 // no hidden field.. - we store the value in 'value', but still display
16303 // display field!!!!
16304 this.lastSelectionText = dv;
16305 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16312 reset : function(){
16313 // overridden so that last data is reset..
16320 this.setValue(this.originalValue);
16321 //this.clearInvalid();
16322 this.lastData = false;
16324 this.view.clearSelections();
16330 findRecord : function(prop, value){
16332 if(this.store.getCount() > 0){
16333 this.store.each(function(r){
16334 if(r.data[prop] == value){
16344 getName: function()
16346 // returns hidden if it's set..
16347 if (!this.rendered) {return ''};
16348 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16352 onViewMove : function(e, t){
16353 this.inKeyMode = false;
16357 onViewOver : function(e, t){
16358 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16361 var item = this.view.findItemFromChild(t);
16364 var index = this.view.indexOf(item);
16365 this.select(index, false);
16370 onViewClick : function(view, doFocus, el, e)
16372 var index = this.view.getSelectedIndexes()[0];
16374 var r = this.store.getAt(index);
16378 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16385 Roo.each(this.tickItems, function(v,k){
16387 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16389 _this.tickItems.splice(k, 1);
16391 if(typeof(e) == 'undefined' && view == false){
16392 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16404 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16405 this.tickItems.push(r.data);
16408 if(typeof(e) == 'undefined' && view == false){
16409 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16416 this.onSelect(r, index);
16418 if(doFocus !== false && !this.blockFocus){
16419 this.inputEl().focus();
16424 restrictHeight : function(){
16425 //this.innerList.dom.style.height = '';
16426 //var inner = this.innerList.dom;
16427 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16428 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16429 //this.list.beginUpdate();
16430 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16431 this.list.alignTo(this.inputEl(), this.listAlign);
16432 this.list.alignTo(this.inputEl(), this.listAlign);
16433 //this.list.endUpdate();
16437 onEmptyResults : function(){
16439 if(this.tickable && this.editable){
16440 this.hasFocus = false;
16441 this.restrictHeight();
16449 * Returns true if the dropdown list is expanded, else false.
16451 isExpanded : function(){
16452 return this.list.isVisible();
16456 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16457 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16458 * @param {String} value The data value of the item to select
16459 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16460 * selected item if it is not currently in view (defaults to true)
16461 * @return {Boolean} True if the value matched an item in the list, else false
16463 selectByValue : function(v, scrollIntoView){
16464 if(v !== undefined && v !== null){
16465 var r = this.findRecord(this.valueField || this.displayField, v);
16467 this.select(this.store.indexOf(r), scrollIntoView);
16475 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16476 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16477 * @param {Number} index The zero-based index of the list item to select
16478 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16479 * selected item if it is not currently in view (defaults to true)
16481 select : function(index, scrollIntoView){
16482 this.selectedIndex = index;
16483 this.view.select(index);
16484 if(scrollIntoView !== false){
16485 var el = this.view.getNode(index);
16487 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16490 this.list.scrollChildIntoView(el, false);
16496 selectNext : function(){
16497 var ct = this.store.getCount();
16499 if(this.selectedIndex == -1){
16501 }else if(this.selectedIndex < ct-1){
16502 this.select(this.selectedIndex+1);
16508 selectPrev : function(){
16509 var ct = this.store.getCount();
16511 if(this.selectedIndex == -1){
16513 }else if(this.selectedIndex != 0){
16514 this.select(this.selectedIndex-1);
16520 onKeyUp : function(e){
16521 if(this.editable !== false && !e.isSpecialKey()){
16522 this.lastKey = e.getKey();
16523 this.dqTask.delay(this.queryDelay);
16528 validateBlur : function(){
16529 return !this.list || !this.list.isVisible();
16533 initQuery : function(){
16535 var v = this.getRawValue();
16537 if(this.tickable && this.editable){
16538 v = this.tickableInputEl().getValue();
16545 doForce : function(){
16546 if(this.inputEl().dom.value.length > 0){
16547 this.inputEl().dom.value =
16548 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16554 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16555 * query allowing the query action to be canceled if needed.
16556 * @param {String} query The SQL query to execute
16557 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16558 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16559 * saved in the current store (defaults to false)
16561 doQuery : function(q, forceAll){
16563 if(q === undefined || q === null){
16568 forceAll: forceAll,
16572 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16577 forceAll = qe.forceAll;
16578 if(forceAll === true || (q.length >= this.minChars)){
16580 this.hasQuery = true;
16582 if(this.lastQuery != q || this.alwaysQuery){
16583 this.lastQuery = q;
16584 if(this.mode == 'local'){
16585 this.selectedIndex = -1;
16587 this.store.clearFilter();
16590 if(this.specialFilter){
16591 this.fireEvent('specialfilter', this);
16596 this.store.filter(this.displayField, q);
16599 this.store.fireEvent("datachanged", this.store);
16606 this.store.baseParams[this.queryParam] = q;
16608 var options = {params : this.getParams(q)};
16611 options.add = true;
16612 options.params.start = this.page * this.pageSize;
16615 this.store.load(options);
16618 * this code will make the page width larger, at the beginning, the list not align correctly,
16619 * we should expand the list on onLoad
16620 * so command out it
16625 this.selectedIndex = -1;
16630 this.loadNext = false;
16634 getParams : function(q){
16636 //p[this.queryParam] = q;
16640 p.limit = this.pageSize;
16646 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16648 collapse : function(){
16649 if(!this.isExpanded()){
16655 this.hasFocus = false;
16659 this.cancelBtn.hide();
16660 this.trigger.show();
16663 this.tickableInputEl().dom.value = '';
16664 this.tickableInputEl().blur();
16669 Roo.get(document).un('mousedown', this.collapseIf, this);
16670 Roo.get(document).un('mousewheel', this.collapseIf, this);
16671 if (!this.editable) {
16672 Roo.get(document).un('keydown', this.listKeyPress, this);
16674 this.fireEvent('collapse', this);
16680 collapseIf : function(e){
16681 var in_combo = e.within(this.el);
16682 var in_list = e.within(this.list);
16683 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16685 if (in_combo || in_list || is_list) {
16686 //e.stopPropagation();
16691 this.onTickableFooterButtonClick(e, false, false);
16699 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16701 expand : function(){
16703 if(this.isExpanded() || !this.hasFocus){
16707 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16708 this.list.setWidth(lw);
16714 this.restrictHeight();
16718 this.tickItems = Roo.apply([], this.item);
16721 this.cancelBtn.show();
16722 this.trigger.hide();
16725 this.tickableInputEl().focus();
16730 Roo.get(document).on('mousedown', this.collapseIf, this);
16731 Roo.get(document).on('mousewheel', this.collapseIf, this);
16732 if (!this.editable) {
16733 Roo.get(document).on('keydown', this.listKeyPress, this);
16736 this.fireEvent('expand', this);
16740 // Implements the default empty TriggerField.onTriggerClick function
16741 onTriggerClick : function(e)
16743 Roo.log('trigger click');
16745 if(this.disabled || !this.triggerList){
16750 this.loadNext = false;
16752 if(this.isExpanded()){
16754 if (!this.blockFocus) {
16755 this.inputEl().focus();
16759 this.hasFocus = true;
16760 if(this.triggerAction == 'all') {
16761 this.doQuery(this.allQuery, true);
16763 this.doQuery(this.getRawValue());
16765 if (!this.blockFocus) {
16766 this.inputEl().focus();
16771 onTickableTriggerClick : function(e)
16778 this.loadNext = false;
16779 this.hasFocus = true;
16781 if(this.triggerAction == 'all') {
16782 this.doQuery(this.allQuery, true);
16784 this.doQuery(this.getRawValue());
16788 onSearchFieldClick : function(e)
16790 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16791 this.onTickableFooterButtonClick(e, false, false);
16795 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16800 this.loadNext = false;
16801 this.hasFocus = true;
16803 if(this.triggerAction == 'all') {
16804 this.doQuery(this.allQuery, true);
16806 this.doQuery(this.getRawValue());
16810 listKeyPress : function(e)
16812 //Roo.log('listkeypress');
16813 // scroll to first matching element based on key pres..
16814 if (e.isSpecialKey()) {
16817 var k = String.fromCharCode(e.getKey()).toUpperCase();
16820 var csel = this.view.getSelectedNodes();
16821 var cselitem = false;
16823 var ix = this.view.indexOf(csel[0]);
16824 cselitem = this.store.getAt(ix);
16825 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16831 this.store.each(function(v) {
16833 // start at existing selection.
16834 if (cselitem.id == v.id) {
16840 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16841 match = this.store.indexOf(v);
16847 if (match === false) {
16848 return true; // no more action?
16851 this.view.select(match);
16852 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16853 sn.scrollIntoView(sn.dom.parentNode, false);
16856 onViewScroll : function(e, t){
16858 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){
16862 this.hasQuery = true;
16864 this.loading = this.list.select('.loading', true).first();
16866 if(this.loading === null){
16867 this.list.createChild({
16869 cls: 'loading roo-select2-more-results roo-select2-active',
16870 html: 'Loading more results...'
16873 this.loading = this.list.select('.loading', true).first();
16875 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16877 this.loading.hide();
16880 this.loading.show();
16885 this.loadNext = true;
16887 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16892 addItem : function(o)
16894 var dv = ''; // display value
16896 if (this.displayField) {
16897 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16899 // this is an error condition!!!
16900 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16907 var choice = this.choices.createChild({
16909 cls: 'roo-select2-search-choice',
16918 cls: 'roo-select2-search-choice-close fa fa-times',
16923 }, this.searchField);
16925 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16927 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16935 this.inputEl().dom.value = '';
16940 onRemoveItem : function(e, _self, o)
16942 e.preventDefault();
16944 this.lastItem = Roo.apply([], this.item);
16946 var index = this.item.indexOf(o.data) * 1;
16949 Roo.log('not this item?!');
16953 this.item.splice(index, 1);
16958 this.fireEvent('remove', this, e);
16964 syncValue : function()
16966 if(!this.item.length){
16973 Roo.each(this.item, function(i){
16974 if(_this.valueField){
16975 value.push(i[_this.valueField]);
16982 this.value = value.join(',');
16984 if(this.hiddenField){
16985 this.hiddenField.dom.value = this.value;
16988 this.store.fireEvent("datachanged", this.store);
16993 clearItem : function()
16995 if(!this.multiple){
17001 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17009 if(this.tickable && !Roo.isTouch){
17010 this.view.refresh();
17014 inputEl: function ()
17016 if(Roo.isIOS && this.useNativeIOS){
17017 return this.el.select('select.roo-ios-select', true).first();
17020 if(Roo.isTouch && this.mobileTouchView){
17021 return this.el.select('input.form-control',true).first();
17025 return this.searchField;
17028 return this.el.select('input.form-control',true).first();
17031 onTickableFooterButtonClick : function(e, btn, el)
17033 e.preventDefault();
17035 this.lastItem = Roo.apply([], this.item);
17037 if(btn && btn.name == 'cancel'){
17038 this.tickItems = Roo.apply([], this.item);
17047 Roo.each(this.tickItems, function(o){
17055 validate : function()
17057 if(this.getVisibilityEl().hasClass('hidden')){
17061 var v = this.getRawValue();
17064 v = this.getValue();
17067 if(this.disabled || this.allowBlank || v.length){
17072 this.markInvalid();
17076 tickableInputEl : function()
17078 if(!this.tickable || !this.editable){
17079 return this.inputEl();
17082 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17086 getAutoCreateTouchView : function()
17091 cls: 'form-group' //input-group
17097 type : this.inputType,
17098 cls : 'form-control x-combo-noedit',
17099 autocomplete: 'new-password',
17100 placeholder : this.placeholder || '',
17105 input.name = this.name;
17109 input.cls += ' input-' + this.size;
17112 if (this.disabled) {
17113 input.disabled = true;
17117 cls : 'roo-combobox-wrap',
17124 inputblock.cls += ' input-group';
17126 inputblock.cn.unshift({
17128 cls : 'input-group-addon input-group-prepend input-group-text',
17133 if(this.removable && !this.multiple){
17134 inputblock.cls += ' roo-removable';
17136 inputblock.cn.push({
17139 cls : 'roo-combo-removable-btn close'
17143 if(this.hasFeedback && !this.allowBlank){
17145 inputblock.cls += ' has-feedback';
17147 inputblock.cn.push({
17149 cls: 'glyphicon form-control-feedback'
17156 inputblock.cls += (this.before) ? '' : ' input-group';
17158 inputblock.cn.push({
17160 cls : 'input-group-addon input-group-append input-group-text',
17166 var ibwrap = inputblock;
17171 cls: 'roo-select2-choices',
17175 cls: 'roo-select2-search-field',
17188 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17193 cls: 'form-hidden-field'
17199 if(!this.multiple && this.showToggleBtn){
17205 if (this.caret != false) {
17208 cls: 'fa fa-' + this.caret
17215 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17217 Roo.bootstrap.version == 3 ? caret : '',
17220 cls: 'combobox-clear',
17234 combobox.cls += ' roo-select2-container-multi';
17237 var align = this.labelAlign || this.parentLabelAlign();
17239 if (align ==='left' && this.fieldLabel.length) {
17244 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17245 tooltip : 'This field is required'
17249 cls : 'control-label col-form-label',
17250 html : this.fieldLabel
17254 cls : 'roo-combobox-wrap ',
17261 var labelCfg = cfg.cn[1];
17262 var contentCfg = cfg.cn[2];
17265 if(this.indicatorpos == 'right'){
17270 cls : 'control-label col-form-label',
17274 html : this.fieldLabel
17278 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17279 tooltip : 'This field is required'
17284 cls : "roo-combobox-wrap ",
17292 labelCfg = cfg.cn[0];
17293 contentCfg = cfg.cn[1];
17298 if(this.labelWidth > 12){
17299 labelCfg.style = "width: " + this.labelWidth + 'px';
17302 if(this.labelWidth < 13 && this.labelmd == 0){
17303 this.labelmd = this.labelWidth;
17306 if(this.labellg > 0){
17307 labelCfg.cls += ' col-lg-' + this.labellg;
17308 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17311 if(this.labelmd > 0){
17312 labelCfg.cls += ' col-md-' + this.labelmd;
17313 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17316 if(this.labelsm > 0){
17317 labelCfg.cls += ' col-sm-' + this.labelsm;
17318 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17321 if(this.labelxs > 0){
17322 labelCfg.cls += ' col-xs-' + this.labelxs;
17323 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17327 } else if ( this.fieldLabel.length) {
17331 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17332 tooltip : 'This field is required'
17336 cls : 'control-label',
17337 html : this.fieldLabel
17348 if(this.indicatorpos == 'right'){
17352 cls : 'control-label',
17353 html : this.fieldLabel,
17357 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17358 tooltip : 'This field is required'
17375 var settings = this;
17377 ['xs','sm','md','lg'].map(function(size){
17378 if (settings[size]) {
17379 cfg.cls += ' col-' + size + '-' + settings[size];
17386 initTouchView : function()
17388 this.renderTouchView();
17390 this.touchViewEl.on('scroll', function(){
17391 this.el.dom.scrollTop = 0;
17394 this.originalValue = this.getValue();
17396 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17398 this.inputEl().on("click", this.showTouchView, this);
17399 if (this.triggerEl) {
17400 this.triggerEl.on("click", this.showTouchView, this);
17404 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17405 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17407 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17409 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17410 this.store.on('load', this.onTouchViewLoad, this);
17411 this.store.on('loadexception', this.onTouchViewLoadException, this);
17413 if(this.hiddenName){
17415 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17417 this.hiddenField.dom.value =
17418 this.hiddenValue !== undefined ? this.hiddenValue :
17419 this.value !== undefined ? this.value : '';
17421 this.el.dom.removeAttribute('name');
17422 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17426 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17427 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17430 if(this.removable && !this.multiple){
17431 var close = this.closeTriggerEl();
17433 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17434 close.on('click', this.removeBtnClick, this, close);
17438 * fix the bug in Safari iOS8
17440 this.inputEl().on("focus", function(e){
17441 document.activeElement.blur();
17444 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17451 renderTouchView : function()
17453 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17454 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17456 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17457 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17459 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17460 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17461 this.touchViewBodyEl.setStyle('overflow', 'auto');
17463 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17464 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17466 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17467 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17471 showTouchView : function()
17477 this.touchViewHeaderEl.hide();
17479 if(this.modalTitle.length){
17480 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17481 this.touchViewHeaderEl.show();
17484 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17485 this.touchViewEl.show();
17487 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17489 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17490 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17492 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17494 if(this.modalTitle.length){
17495 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17498 this.touchViewBodyEl.setHeight(bodyHeight);
17502 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17504 this.touchViewEl.addClass(['in','show']);
17507 if(this._touchViewMask){
17508 Roo.get(document.body).addClass("x-body-masked");
17509 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17510 this._touchViewMask.setStyle('z-index', 10000);
17511 this._touchViewMask.addClass('show');
17514 this.doTouchViewQuery();
17518 hideTouchView : function()
17520 this.touchViewEl.removeClass(['in','show']);
17524 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17526 this.touchViewEl.setStyle('display', 'none');
17529 if(this._touchViewMask){
17530 this._touchViewMask.removeClass('show');
17531 Roo.get(document.body).removeClass("x-body-masked");
17535 setTouchViewValue : function()
17542 Roo.each(this.tickItems, function(o){
17547 this.hideTouchView();
17550 doTouchViewQuery : function()
17559 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17563 if(!this.alwaysQuery || this.mode == 'local'){
17564 this.onTouchViewLoad();
17571 onTouchViewBeforeLoad : function(combo,opts)
17577 onTouchViewLoad : function()
17579 if(this.store.getCount() < 1){
17580 this.onTouchViewEmptyResults();
17584 this.clearTouchView();
17586 var rawValue = this.getRawValue();
17588 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17590 this.tickItems = [];
17592 this.store.data.each(function(d, rowIndex){
17593 var row = this.touchViewListGroup.createChild(template);
17595 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17596 row.addClass(d.data.cls);
17599 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17602 html : d.data[this.displayField]
17605 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17606 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17609 row.removeClass('selected');
17610 if(!this.multiple && this.valueField &&
17611 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17614 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17615 row.addClass('selected');
17618 if(this.multiple && this.valueField &&
17619 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17623 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17624 this.tickItems.push(d.data);
17627 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17631 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17633 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17635 if(this.modalTitle.length){
17636 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17639 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17641 if(this.mobile_restrict_height && listHeight < bodyHeight){
17642 this.touchViewBodyEl.setHeight(listHeight);
17647 if(firstChecked && listHeight > bodyHeight){
17648 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17653 onTouchViewLoadException : function()
17655 this.hideTouchView();
17658 onTouchViewEmptyResults : function()
17660 this.clearTouchView();
17662 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17664 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17668 clearTouchView : function()
17670 this.touchViewListGroup.dom.innerHTML = '';
17673 onTouchViewClick : function(e, el, o)
17675 e.preventDefault();
17678 var rowIndex = o.rowIndex;
17680 var r = this.store.getAt(rowIndex);
17682 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17684 if(!this.multiple){
17685 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17686 c.dom.removeAttribute('checked');
17689 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17691 this.setFromData(r.data);
17693 var close = this.closeTriggerEl();
17699 this.hideTouchView();
17701 this.fireEvent('select', this, r, rowIndex);
17706 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17707 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17708 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17712 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17713 this.addItem(r.data);
17714 this.tickItems.push(r.data);
17718 getAutoCreateNativeIOS : function()
17721 cls: 'form-group' //input-group,
17726 cls : 'roo-ios-select'
17730 combobox.name = this.name;
17733 if (this.disabled) {
17734 combobox.disabled = true;
17737 var settings = this;
17739 ['xs','sm','md','lg'].map(function(size){
17740 if (settings[size]) {
17741 cfg.cls += ' col-' + size + '-' + settings[size];
17751 initIOSView : function()
17753 this.store.on('load', this.onIOSViewLoad, this);
17758 onIOSViewLoad : function()
17760 if(this.store.getCount() < 1){
17764 this.clearIOSView();
17766 if(this.allowBlank) {
17768 var default_text = '-- SELECT --';
17770 if(this.placeholder.length){
17771 default_text = this.placeholder;
17774 if(this.emptyTitle.length){
17775 default_text += ' - ' + this.emptyTitle + ' -';
17778 var opt = this.inputEl().createChild({
17781 html : default_text
17785 o[this.valueField] = 0;
17786 o[this.displayField] = default_text;
17788 this.ios_options.push({
17795 this.store.data.each(function(d, rowIndex){
17799 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17800 html = d.data[this.displayField];
17805 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17806 value = d.data[this.valueField];
17815 if(this.value == d.data[this.valueField]){
17816 option['selected'] = true;
17819 var opt = this.inputEl().createChild(option);
17821 this.ios_options.push({
17828 this.inputEl().on('change', function(){
17829 this.fireEvent('select', this);
17834 clearIOSView: function()
17836 this.inputEl().dom.innerHTML = '';
17838 this.ios_options = [];
17841 setIOSValue: function(v)
17845 if(!this.ios_options){
17849 Roo.each(this.ios_options, function(opts){
17851 opts.el.dom.removeAttribute('selected');
17853 if(opts.data[this.valueField] != v){
17857 opts.el.dom.setAttribute('selected', true);
17863 * @cfg {Boolean} grow
17867 * @cfg {Number} growMin
17871 * @cfg {Number} growMax
17880 Roo.apply(Roo.bootstrap.ComboBox, {
17884 cls: 'modal-header',
17906 cls: 'list-group-item',
17910 cls: 'roo-combobox-list-group-item-value'
17914 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17928 listItemCheckbox : {
17930 cls: 'list-group-item',
17934 cls: 'roo-combobox-list-group-item-value'
17938 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17954 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17959 cls: 'modal-footer',
17967 cls: 'col-xs-6 text-left',
17970 cls: 'btn btn-danger roo-touch-view-cancel',
17976 cls: 'col-xs-6 text-right',
17979 cls: 'btn btn-success roo-touch-view-ok',
17990 Roo.apply(Roo.bootstrap.ComboBox, {
17992 touchViewTemplate : {
17994 cls: 'modal fade roo-combobox-touch-view',
17998 cls: 'modal-dialog',
17999 style : 'position:fixed', // we have to fix position....
18003 cls: 'modal-content',
18005 Roo.bootstrap.ComboBox.header,
18006 Roo.bootstrap.ComboBox.body,
18007 Roo.bootstrap.ComboBox.footer
18016 * Ext JS Library 1.1.1
18017 * Copyright(c) 2006-2007, Ext JS, LLC.
18019 * Originally Released Under LGPL - original licence link has changed is not relivant.
18022 * <script type="text/javascript">
18027 * @extends Roo.util.Observable
18028 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18029 * This class also supports single and multi selection modes. <br>
18030 * Create a data model bound view:
18032 var store = new Roo.data.Store(...);
18034 var view = new Roo.View({
18036 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18038 singleSelect: true,
18039 selectedClass: "ydataview-selected",
18043 // listen for node click?
18044 view.on("click", function(vw, index, node, e){
18045 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18049 dataModel.load("foobar.xml");
18051 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18053 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18054 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18056 * Note: old style constructor is still suported (container, template, config)
18059 * Create a new View
18060 * @param {Object} config The config object
18063 Roo.View = function(config, depreciated_tpl, depreciated_config){
18065 this.parent = false;
18067 if (typeof(depreciated_tpl) == 'undefined') {
18068 // new way.. - universal constructor.
18069 Roo.apply(this, config);
18070 this.el = Roo.get(this.el);
18073 this.el = Roo.get(config);
18074 this.tpl = depreciated_tpl;
18075 Roo.apply(this, depreciated_config);
18077 this.wrapEl = this.el.wrap().wrap();
18078 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18081 if(typeof(this.tpl) == "string"){
18082 this.tpl = new Roo.Template(this.tpl);
18084 // support xtype ctors..
18085 this.tpl = new Roo.factory(this.tpl, Roo);
18089 this.tpl.compile();
18094 * @event beforeclick
18095 * Fires before a click is processed. Returns false to cancel the default action.
18096 * @param {Roo.View} this
18097 * @param {Number} index The index of the target node
18098 * @param {HTMLElement} node The target node
18099 * @param {Roo.EventObject} e The raw event object
18101 "beforeclick" : true,
18104 * Fires when a template node is clicked.
18105 * @param {Roo.View} this
18106 * @param {Number} index The index of the target node
18107 * @param {HTMLElement} node The target node
18108 * @param {Roo.EventObject} e The raw event object
18113 * Fires when a template node is double clicked.
18114 * @param {Roo.View} this
18115 * @param {Number} index The index of the target node
18116 * @param {HTMLElement} node The target node
18117 * @param {Roo.EventObject} e The raw event object
18121 * @event contextmenu
18122 * Fires when a template node is right clicked.
18123 * @param {Roo.View} this
18124 * @param {Number} index The index of the target node
18125 * @param {HTMLElement} node The target node
18126 * @param {Roo.EventObject} e The raw event object
18128 "contextmenu" : true,
18130 * @event selectionchange
18131 * Fires when the selected nodes change.
18132 * @param {Roo.View} this
18133 * @param {Array} selections Array of the selected nodes
18135 "selectionchange" : true,
18138 * @event beforeselect
18139 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18140 * @param {Roo.View} this
18141 * @param {HTMLElement} node The node to be selected
18142 * @param {Array} selections Array of currently selected nodes
18144 "beforeselect" : true,
18146 * @event preparedata
18147 * Fires on every row to render, to allow you to change the data.
18148 * @param {Roo.View} this
18149 * @param {Object} data to be rendered (change this)
18151 "preparedata" : true
18159 "click": this.onClick,
18160 "dblclick": this.onDblClick,
18161 "contextmenu": this.onContextMenu,
18165 this.selections = [];
18167 this.cmp = new Roo.CompositeElementLite([]);
18169 this.store = Roo.factory(this.store, Roo.data);
18170 this.setStore(this.store, true);
18173 if ( this.footer && this.footer.xtype) {
18175 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18177 this.footer.dataSource = this.store;
18178 this.footer.container = fctr;
18179 this.footer = Roo.factory(this.footer, Roo);
18180 fctr.insertFirst(this.el);
18182 // this is a bit insane - as the paging toolbar seems to detach the el..
18183 // dom.parentNode.parentNode.parentNode
18184 // they get detached?
18188 Roo.View.superclass.constructor.call(this);
18193 Roo.extend(Roo.View, Roo.util.Observable, {
18196 * @cfg {Roo.data.Store} store Data store to load data from.
18201 * @cfg {String|Roo.Element} el The container element.
18206 * @cfg {String|Roo.Template} tpl The template used by this View
18210 * @cfg {String} dataName the named area of the template to use as the data area
18211 * Works with domtemplates roo-name="name"
18215 * @cfg {String} selectedClass The css class to add to selected nodes
18217 selectedClass : "x-view-selected",
18219 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18224 * @cfg {String} text to display on mask (default Loading)
18228 * @cfg {Boolean} multiSelect Allow multiple selection
18230 multiSelect : false,
18232 * @cfg {Boolean} singleSelect Allow single selection
18234 singleSelect: false,
18237 * @cfg {Boolean} toggleSelect - selecting
18239 toggleSelect : false,
18242 * @cfg {Boolean} tickable - selecting
18247 * Returns the element this view is bound to.
18248 * @return {Roo.Element}
18250 getEl : function(){
18251 return this.wrapEl;
18257 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18259 refresh : function(){
18260 //Roo.log('refresh');
18263 // if we are using something like 'domtemplate', then
18264 // the what gets used is:
18265 // t.applySubtemplate(NAME, data, wrapping data..)
18266 // the outer template then get' applied with
18267 // the store 'extra data'
18268 // and the body get's added to the
18269 // roo-name="data" node?
18270 // <span class='roo-tpl-{name}'></span> ?????
18274 this.clearSelections();
18275 this.el.update("");
18277 var records = this.store.getRange();
18278 if(records.length < 1) {
18280 // is this valid?? = should it render a template??
18282 this.el.update(this.emptyText);
18286 if (this.dataName) {
18287 this.el.update(t.apply(this.store.meta)); //????
18288 el = this.el.child('.roo-tpl-' + this.dataName);
18291 for(var i = 0, len = records.length; i < len; i++){
18292 var data = this.prepareData(records[i].data, i, records[i]);
18293 this.fireEvent("preparedata", this, data, i, records[i]);
18295 var d = Roo.apply({}, data);
18298 Roo.apply(d, {'roo-id' : Roo.id()});
18302 Roo.each(this.parent.item, function(item){
18303 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18306 Roo.apply(d, {'roo-data-checked' : 'checked'});
18310 html[html.length] = Roo.util.Format.trim(
18312 t.applySubtemplate(this.dataName, d, this.store.meta) :
18319 el.update(html.join(""));
18320 this.nodes = el.dom.childNodes;
18321 this.updateIndexes(0);
18326 * Function to override to reformat the data that is sent to
18327 * the template for each node.
18328 * DEPRICATED - use the preparedata event handler.
18329 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18330 * a JSON object for an UpdateManager bound view).
18332 prepareData : function(data, index, record)
18334 this.fireEvent("preparedata", this, data, index, record);
18338 onUpdate : function(ds, record){
18339 // Roo.log('on update');
18340 this.clearSelections();
18341 var index = this.store.indexOf(record);
18342 var n = this.nodes[index];
18343 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18344 n.parentNode.removeChild(n);
18345 this.updateIndexes(index, index);
18351 onAdd : function(ds, records, index)
18353 //Roo.log(['on Add', ds, records, index] );
18354 this.clearSelections();
18355 if(this.nodes.length == 0){
18359 var n = this.nodes[index];
18360 for(var i = 0, len = records.length; i < len; i++){
18361 var d = this.prepareData(records[i].data, i, records[i]);
18363 this.tpl.insertBefore(n, d);
18366 this.tpl.append(this.el, d);
18369 this.updateIndexes(index);
18372 onRemove : function(ds, record, index){
18373 // Roo.log('onRemove');
18374 this.clearSelections();
18375 var el = this.dataName ?
18376 this.el.child('.roo-tpl-' + this.dataName) :
18379 el.dom.removeChild(this.nodes[index]);
18380 this.updateIndexes(index);
18384 * Refresh an individual node.
18385 * @param {Number} index
18387 refreshNode : function(index){
18388 this.onUpdate(this.store, this.store.getAt(index));
18391 updateIndexes : function(startIndex, endIndex){
18392 var ns = this.nodes;
18393 startIndex = startIndex || 0;
18394 endIndex = endIndex || ns.length - 1;
18395 for(var i = startIndex; i <= endIndex; i++){
18396 ns[i].nodeIndex = i;
18401 * Changes the data store this view uses and refresh the view.
18402 * @param {Store} store
18404 setStore : function(store, initial){
18405 if(!initial && this.store){
18406 this.store.un("datachanged", this.refresh);
18407 this.store.un("add", this.onAdd);
18408 this.store.un("remove", this.onRemove);
18409 this.store.un("update", this.onUpdate);
18410 this.store.un("clear", this.refresh);
18411 this.store.un("beforeload", this.onBeforeLoad);
18412 this.store.un("load", this.onLoad);
18413 this.store.un("loadexception", this.onLoad);
18417 store.on("datachanged", this.refresh, this);
18418 store.on("add", this.onAdd, this);
18419 store.on("remove", this.onRemove, this);
18420 store.on("update", this.onUpdate, this);
18421 store.on("clear", this.refresh, this);
18422 store.on("beforeload", this.onBeforeLoad, this);
18423 store.on("load", this.onLoad, this);
18424 store.on("loadexception", this.onLoad, this);
18432 * onbeforeLoad - masks the loading area.
18435 onBeforeLoad : function(store,opts)
18437 //Roo.log('onBeforeLoad');
18439 this.el.update("");
18441 this.el.mask(this.mask ? this.mask : "Loading" );
18443 onLoad : function ()
18450 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18451 * @param {HTMLElement} node
18452 * @return {HTMLElement} The template node
18454 findItemFromChild : function(node){
18455 var el = this.dataName ?
18456 this.el.child('.roo-tpl-' + this.dataName,true) :
18459 if(!node || node.parentNode == el){
18462 var p = node.parentNode;
18463 while(p && p != el){
18464 if(p.parentNode == el){
18473 onClick : function(e){
18474 var item = this.findItemFromChild(e.getTarget());
18476 var index = this.indexOf(item);
18477 if(this.onItemClick(item, index, e) !== false){
18478 this.fireEvent("click", this, index, item, e);
18481 this.clearSelections();
18486 onContextMenu : function(e){
18487 var item = this.findItemFromChild(e.getTarget());
18489 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18494 onDblClick : function(e){
18495 var item = this.findItemFromChild(e.getTarget());
18497 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18501 onItemClick : function(item, index, e)
18503 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18506 if (this.toggleSelect) {
18507 var m = this.isSelected(item) ? 'unselect' : 'select';
18510 _t[m](item, true, false);
18513 if(this.multiSelect || this.singleSelect){
18514 if(this.multiSelect && e.shiftKey && this.lastSelection){
18515 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18517 this.select(item, this.multiSelect && e.ctrlKey);
18518 this.lastSelection = item;
18521 if(!this.tickable){
18522 e.preventDefault();
18530 * Get the number of selected nodes.
18533 getSelectionCount : function(){
18534 return this.selections.length;
18538 * Get the currently selected nodes.
18539 * @return {Array} An array of HTMLElements
18541 getSelectedNodes : function(){
18542 return this.selections;
18546 * Get the indexes of the selected nodes.
18549 getSelectedIndexes : function(){
18550 var indexes = [], s = this.selections;
18551 for(var i = 0, len = s.length; i < len; i++){
18552 indexes.push(s[i].nodeIndex);
18558 * Clear all selections
18559 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18561 clearSelections : function(suppressEvent){
18562 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18563 this.cmp.elements = this.selections;
18564 this.cmp.removeClass(this.selectedClass);
18565 this.selections = [];
18566 if(!suppressEvent){
18567 this.fireEvent("selectionchange", this, this.selections);
18573 * Returns true if the passed node is selected
18574 * @param {HTMLElement/Number} node The node or node index
18575 * @return {Boolean}
18577 isSelected : function(node){
18578 var s = this.selections;
18582 node = this.getNode(node);
18583 return s.indexOf(node) !== -1;
18588 * @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
18589 * @param {Boolean} keepExisting (optional) true to keep existing selections
18590 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18592 select : function(nodeInfo, keepExisting, suppressEvent){
18593 if(nodeInfo instanceof Array){
18595 this.clearSelections(true);
18597 for(var i = 0, len = nodeInfo.length; i < len; i++){
18598 this.select(nodeInfo[i], true, true);
18602 var node = this.getNode(nodeInfo);
18603 if(!node || this.isSelected(node)){
18604 return; // already selected.
18607 this.clearSelections(true);
18610 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18611 Roo.fly(node).addClass(this.selectedClass);
18612 this.selections.push(node);
18613 if(!suppressEvent){
18614 this.fireEvent("selectionchange", this, this.selections);
18622 * @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
18623 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18624 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18626 unselect : function(nodeInfo, keepExisting, suppressEvent)
18628 if(nodeInfo instanceof Array){
18629 Roo.each(this.selections, function(s) {
18630 this.unselect(s, nodeInfo);
18634 var node = this.getNode(nodeInfo);
18635 if(!node || !this.isSelected(node)){
18636 //Roo.log("not selected");
18637 return; // not selected.
18641 Roo.each(this.selections, function(s) {
18643 Roo.fly(node).removeClass(this.selectedClass);
18650 this.selections= ns;
18651 this.fireEvent("selectionchange", this, this.selections);
18655 * Gets a template node.
18656 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18657 * @return {HTMLElement} The node or null if it wasn't found
18659 getNode : function(nodeInfo){
18660 if(typeof nodeInfo == "string"){
18661 return document.getElementById(nodeInfo);
18662 }else if(typeof nodeInfo == "number"){
18663 return this.nodes[nodeInfo];
18669 * Gets a range template nodes.
18670 * @param {Number} startIndex
18671 * @param {Number} endIndex
18672 * @return {Array} An array of nodes
18674 getNodes : function(start, end){
18675 var ns = this.nodes;
18676 start = start || 0;
18677 end = typeof end == "undefined" ? ns.length - 1 : end;
18680 for(var i = start; i <= end; i++){
18684 for(var i = start; i >= end; i--){
18692 * Finds the index of the passed node
18693 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18694 * @return {Number} The index of the node or -1
18696 indexOf : function(node){
18697 node = this.getNode(node);
18698 if(typeof node.nodeIndex == "number"){
18699 return node.nodeIndex;
18701 var ns = this.nodes;
18702 for(var i = 0, len = ns.length; i < len; i++){
18713 * based on jquery fullcalendar
18717 Roo.bootstrap = Roo.bootstrap || {};
18719 * @class Roo.bootstrap.Calendar
18720 * @extends Roo.bootstrap.Component
18721 * Bootstrap Calendar class
18722 * @cfg {Boolean} loadMask (true|false) default false
18723 * @cfg {Object} header generate the user specific header of the calendar, default false
18726 * Create a new Container
18727 * @param {Object} config The config object
18732 Roo.bootstrap.Calendar = function(config){
18733 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18737 * Fires when a date is selected
18738 * @param {DatePicker} this
18739 * @param {Date} date The selected date
18743 * @event monthchange
18744 * Fires when the displayed month changes
18745 * @param {DatePicker} this
18746 * @param {Date} date The selected month
18748 'monthchange': true,
18750 * @event evententer
18751 * Fires when mouse over an event
18752 * @param {Calendar} this
18753 * @param {event} Event
18755 'evententer': true,
18757 * @event eventleave
18758 * Fires when the mouse leaves an
18759 * @param {Calendar} this
18762 'eventleave': true,
18764 * @event eventclick
18765 * Fires when the mouse click an
18766 * @param {Calendar} this
18775 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18778 * @cfg {Number} startDay
18779 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18787 getAutoCreate : function(){
18790 var fc_button = function(name, corner, style, content ) {
18791 return Roo.apply({},{
18793 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18795 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18798 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18809 style : 'width:100%',
18816 cls : 'fc-header-left',
18818 fc_button('prev', 'left', 'arrow', '‹' ),
18819 fc_button('next', 'right', 'arrow', '›' ),
18820 { tag: 'span', cls: 'fc-header-space' },
18821 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18829 cls : 'fc-header-center',
18833 cls: 'fc-header-title',
18836 html : 'month / year'
18844 cls : 'fc-header-right',
18846 /* fc_button('month', 'left', '', 'month' ),
18847 fc_button('week', '', '', 'week' ),
18848 fc_button('day', 'right', '', 'day' )
18860 header = this.header;
18863 var cal_heads = function() {
18865 // fixme - handle this.
18867 for (var i =0; i < Date.dayNames.length; i++) {
18868 var d = Date.dayNames[i];
18871 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18872 html : d.substring(0,3)
18876 ret[0].cls += ' fc-first';
18877 ret[6].cls += ' fc-last';
18880 var cal_cell = function(n) {
18883 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18888 cls: 'fc-day-number',
18892 cls: 'fc-day-content',
18896 style: 'position: relative;' // height: 17px;
18908 var cal_rows = function() {
18911 for (var r = 0; r < 6; r++) {
18918 for (var i =0; i < Date.dayNames.length; i++) {
18919 var d = Date.dayNames[i];
18920 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18923 row.cn[0].cls+=' fc-first';
18924 row.cn[0].cn[0].style = 'min-height:90px';
18925 row.cn[6].cls+=' fc-last';
18929 ret[0].cls += ' fc-first';
18930 ret[4].cls += ' fc-prev-last';
18931 ret[5].cls += ' fc-last';
18938 cls: 'fc-border-separate',
18939 style : 'width:100%',
18947 cls : 'fc-first fc-last',
18965 cls : 'fc-content',
18966 style : "position: relative;",
18969 cls : 'fc-view fc-view-month fc-grid',
18970 style : 'position: relative',
18971 unselectable : 'on',
18974 cls : 'fc-event-container',
18975 style : 'position:absolute;z-index:8;top:0;left:0;'
18993 initEvents : function()
18996 throw "can not find store for calendar";
19002 style: "text-align:center",
19006 style: "background-color:white;width:50%;margin:250 auto",
19010 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19021 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19023 var size = this.el.select('.fc-content', true).first().getSize();
19024 this.maskEl.setSize(size.width, size.height);
19025 this.maskEl.enableDisplayMode("block");
19026 if(!this.loadMask){
19027 this.maskEl.hide();
19030 this.store = Roo.factory(this.store, Roo.data);
19031 this.store.on('load', this.onLoad, this);
19032 this.store.on('beforeload', this.onBeforeLoad, this);
19036 this.cells = this.el.select('.fc-day',true);
19037 //Roo.log(this.cells);
19038 this.textNodes = this.el.query('.fc-day-number');
19039 this.cells.addClassOnOver('fc-state-hover');
19041 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19042 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19043 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19044 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19046 this.on('monthchange', this.onMonthChange, this);
19048 this.update(new Date().clearTime());
19051 resize : function() {
19052 var sz = this.el.getSize();
19054 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19055 this.el.select('.fc-day-content div',true).setHeight(34);
19060 showPrevMonth : function(e){
19061 this.update(this.activeDate.add("mo", -1));
19063 showToday : function(e){
19064 this.update(new Date().clearTime());
19067 showNextMonth : function(e){
19068 this.update(this.activeDate.add("mo", 1));
19072 showPrevYear : function(){
19073 this.update(this.activeDate.add("y", -1));
19077 showNextYear : function(){
19078 this.update(this.activeDate.add("y", 1));
19083 update : function(date)
19085 var vd = this.activeDate;
19086 this.activeDate = date;
19087 // if(vd && this.el){
19088 // var t = date.getTime();
19089 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19090 // Roo.log('using add remove');
19092 // this.fireEvent('monthchange', this, date);
19094 // this.cells.removeClass("fc-state-highlight");
19095 // this.cells.each(function(c){
19096 // if(c.dateValue == t){
19097 // c.addClass("fc-state-highlight");
19098 // setTimeout(function(){
19099 // try{c.dom.firstChild.focus();}catch(e){}
19109 var days = date.getDaysInMonth();
19111 var firstOfMonth = date.getFirstDateOfMonth();
19112 var startingPos = firstOfMonth.getDay()-this.startDay;
19114 if(startingPos < this.startDay){
19118 var pm = date.add(Date.MONTH, -1);
19119 var prevStart = pm.getDaysInMonth()-startingPos;
19121 this.cells = this.el.select('.fc-day',true);
19122 this.textNodes = this.el.query('.fc-day-number');
19123 this.cells.addClassOnOver('fc-state-hover');
19125 var cells = this.cells.elements;
19126 var textEls = this.textNodes;
19128 Roo.each(cells, function(cell){
19129 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19132 days += startingPos;
19134 // convert everything to numbers so it's fast
19135 var day = 86400000;
19136 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19139 //Roo.log(prevStart);
19141 var today = new Date().clearTime().getTime();
19142 var sel = date.clearTime().getTime();
19143 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19144 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19145 var ddMatch = this.disabledDatesRE;
19146 var ddText = this.disabledDatesText;
19147 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19148 var ddaysText = this.disabledDaysText;
19149 var format = this.format;
19151 var setCellClass = function(cal, cell){
19155 //Roo.log('set Cell Class');
19157 var t = d.getTime();
19161 cell.dateValue = t;
19163 cell.className += " fc-today";
19164 cell.className += " fc-state-highlight";
19165 cell.title = cal.todayText;
19168 // disable highlight in other month..
19169 //cell.className += " fc-state-highlight";
19174 cell.className = " fc-state-disabled";
19175 cell.title = cal.minText;
19179 cell.className = " fc-state-disabled";
19180 cell.title = cal.maxText;
19184 if(ddays.indexOf(d.getDay()) != -1){
19185 cell.title = ddaysText;
19186 cell.className = " fc-state-disabled";
19189 if(ddMatch && format){
19190 var fvalue = d.dateFormat(format);
19191 if(ddMatch.test(fvalue)){
19192 cell.title = ddText.replace("%0", fvalue);
19193 cell.className = " fc-state-disabled";
19197 if (!cell.initialClassName) {
19198 cell.initialClassName = cell.dom.className;
19201 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19206 for(; i < startingPos; i++) {
19207 textEls[i].innerHTML = (++prevStart);
19208 d.setDate(d.getDate()+1);
19210 cells[i].className = "fc-past fc-other-month";
19211 setCellClass(this, cells[i]);
19216 for(; i < days; i++){
19217 intDay = i - startingPos + 1;
19218 textEls[i].innerHTML = (intDay);
19219 d.setDate(d.getDate()+1);
19221 cells[i].className = ''; // "x-date-active";
19222 setCellClass(this, cells[i]);
19226 for(; i < 42; i++) {
19227 textEls[i].innerHTML = (++extraDays);
19228 d.setDate(d.getDate()+1);
19230 cells[i].className = "fc-future fc-other-month";
19231 setCellClass(this, cells[i]);
19234 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19236 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19238 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19239 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19241 if(totalRows != 6){
19242 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19243 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19246 this.fireEvent('monthchange', this, date);
19250 if(!this.internalRender){
19251 var main = this.el.dom.firstChild;
19252 var w = main.offsetWidth;
19253 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19254 Roo.fly(main).setWidth(w);
19255 this.internalRender = true;
19256 // opera does not respect the auto grow header center column
19257 // then, after it gets a width opera refuses to recalculate
19258 // without a second pass
19259 if(Roo.isOpera && !this.secondPass){
19260 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19261 this.secondPass = true;
19262 this.update.defer(10, this, [date]);
19269 findCell : function(dt) {
19270 dt = dt.clearTime().getTime();
19272 this.cells.each(function(c){
19273 //Roo.log("check " +c.dateValue + '?=' + dt);
19274 if(c.dateValue == dt){
19284 findCells : function(ev) {
19285 var s = ev.start.clone().clearTime().getTime();
19287 var e= ev.end.clone().clearTime().getTime();
19290 this.cells.each(function(c){
19291 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19293 if(c.dateValue > e){
19296 if(c.dateValue < s){
19305 // findBestRow: function(cells)
19309 // for (var i =0 ; i < cells.length;i++) {
19310 // ret = Math.max(cells[i].rows || 0,ret);
19317 addItem : function(ev)
19319 // look for vertical location slot in
19320 var cells = this.findCells(ev);
19322 // ev.row = this.findBestRow(cells);
19324 // work out the location.
19328 for(var i =0; i < cells.length; i++) {
19330 cells[i].row = cells[0].row;
19333 cells[i].row = cells[i].row + 1;
19343 if (crow.start.getY() == cells[i].getY()) {
19345 crow.end = cells[i];
19362 cells[0].events.push(ev);
19364 this.calevents.push(ev);
19367 clearEvents: function() {
19369 if(!this.calevents){
19373 Roo.each(this.cells.elements, function(c){
19379 Roo.each(this.calevents, function(e) {
19380 Roo.each(e.els, function(el) {
19381 el.un('mouseenter' ,this.onEventEnter, this);
19382 el.un('mouseleave' ,this.onEventLeave, this);
19387 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19393 renderEvents: function()
19397 this.cells.each(function(c) {
19406 if(c.row != c.events.length){
19407 r = 4 - (4 - (c.row - c.events.length));
19410 c.events = ev.slice(0, r);
19411 c.more = ev.slice(r);
19413 if(c.more.length && c.more.length == 1){
19414 c.events.push(c.more.pop());
19417 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19421 this.cells.each(function(c) {
19423 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19426 for (var e = 0; e < c.events.length; e++){
19427 var ev = c.events[e];
19428 var rows = ev.rows;
19430 for(var i = 0; i < rows.length; i++) {
19432 // how many rows should it span..
19435 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19436 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19438 unselectable : "on",
19441 cls: 'fc-event-inner',
19445 // cls: 'fc-event-time',
19446 // html : cells.length > 1 ? '' : ev.time
19450 cls: 'fc-event-title',
19451 html : String.format('{0}', ev.title)
19458 cls: 'ui-resizable-handle ui-resizable-e',
19459 html : '  '
19466 cfg.cls += ' fc-event-start';
19468 if ((i+1) == rows.length) {
19469 cfg.cls += ' fc-event-end';
19472 var ctr = _this.el.select('.fc-event-container',true).first();
19473 var cg = ctr.createChild(cfg);
19475 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19476 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19478 var r = (c.more.length) ? 1 : 0;
19479 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19480 cg.setWidth(ebox.right - sbox.x -2);
19482 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19483 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19484 cg.on('click', _this.onEventClick, _this, ev);
19495 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19496 style : 'position: absolute',
19497 unselectable : "on",
19500 cls: 'fc-event-inner',
19504 cls: 'fc-event-title',
19512 cls: 'ui-resizable-handle ui-resizable-e',
19513 html : '  '
19519 var ctr = _this.el.select('.fc-event-container',true).first();
19520 var cg = ctr.createChild(cfg);
19522 var sbox = c.select('.fc-day-content',true).first().getBox();
19523 var ebox = c.select('.fc-day-content',true).first().getBox();
19525 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19526 cg.setWidth(ebox.right - sbox.x -2);
19528 cg.on('click', _this.onMoreEventClick, _this, c.more);
19538 onEventEnter: function (e, el,event,d) {
19539 this.fireEvent('evententer', this, el, event);
19542 onEventLeave: function (e, el,event,d) {
19543 this.fireEvent('eventleave', this, el, event);
19546 onEventClick: function (e, el,event,d) {
19547 this.fireEvent('eventclick', this, el, event);
19550 onMonthChange: function () {
19554 onMoreEventClick: function(e, el, more)
19558 this.calpopover.placement = 'right';
19559 this.calpopover.setTitle('More');
19561 this.calpopover.setContent('');
19563 var ctr = this.calpopover.el.select('.popover-content', true).first();
19565 Roo.each(more, function(m){
19567 cls : 'fc-event-hori fc-event-draggable',
19570 var cg = ctr.createChild(cfg);
19572 cg.on('click', _this.onEventClick, _this, m);
19575 this.calpopover.show(el);
19580 onLoad: function ()
19582 this.calevents = [];
19585 if(this.store.getCount() > 0){
19586 this.store.data.each(function(d){
19589 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19590 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19591 time : d.data.start_time,
19592 title : d.data.title,
19593 description : d.data.description,
19594 venue : d.data.venue
19599 this.renderEvents();
19601 if(this.calevents.length && this.loadMask){
19602 this.maskEl.hide();
19606 onBeforeLoad: function()
19608 this.clearEvents();
19610 this.maskEl.show();
19624 * @class Roo.bootstrap.Popover
19625 * @extends Roo.bootstrap.Component
19626 * Bootstrap Popover class
19627 * @cfg {String} html contents of the popover (or false to use children..)
19628 * @cfg {String} title of popover (or false to hide)
19629 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19630 * @cfg {String} trigger click || hover (or false to trigger manually)
19631 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19632 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19633 * - if false and it has a 'parent' then it will be automatically added to that element
19634 * - if string - Roo.get will be called
19635 * @cfg {Number} delay - delay before showing
19638 * Create a new Popover
19639 * @param {Object} config The config object
19642 Roo.bootstrap.Popover = function(config){
19643 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19649 * After the popover show
19651 * @param {Roo.bootstrap.Popover} this
19656 * After the popover hide
19658 * @param {Roo.bootstrap.Popover} this
19664 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19669 placement : 'right',
19670 trigger : 'hover', // hover
19676 can_build_overlaid : false,
19678 maskEl : false, // the mask element
19681 alignEl : false, // when show is called with an element - this get's stored.
19683 getChildContainer : function()
19685 return this.contentEl;
19688 getPopoverHeader : function()
19690 this.title = true; // flag not to hide it..
19691 this.headerEl.addClass('p-0');
19692 return this.headerEl
19696 getAutoCreate : function(){
19699 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19700 style: 'display:block',
19706 cls : 'popover-inner ',
19710 cls: 'popover-title popover-header',
19711 html : this.title === false ? '' : this.title
19714 cls : 'popover-content popover-body ' + (this.cls || ''),
19715 html : this.html || ''
19726 * @param {string} the title
19728 setTitle: function(str)
19732 this.headerEl.dom.innerHTML = str;
19737 * @param {string} the body content
19739 setContent: function(str)
19742 if (this.contentEl) {
19743 this.contentEl.dom.innerHTML = str;
19747 // as it get's added to the bottom of the page.
19748 onRender : function(ct, position)
19750 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19755 var cfg = Roo.apply({}, this.getAutoCreate());
19759 cfg.cls += ' ' + this.cls;
19762 cfg.style = this.style;
19764 //Roo.log("adding to ");
19765 this.el = Roo.get(document.body).createChild(cfg, position);
19766 // Roo.log(this.el);
19769 this.contentEl = this.el.select('.popover-content',true).first();
19770 this.headerEl = this.el.select('.popover-title',true).first();
19773 if(typeof(this.items) != 'undefined'){
19774 var items = this.items;
19777 for(var i =0;i < items.length;i++) {
19778 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19782 this.items = nitems;
19784 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19785 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19792 resizeMask : function()
19794 this.maskEl.setSize(
19795 Roo.lib.Dom.getViewWidth(true),
19796 Roo.lib.Dom.getViewHeight(true)
19800 initEvents : function()
19804 Roo.bootstrap.Popover.register(this);
19807 this.arrowEl = this.el.select('.arrow',true).first();
19808 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19809 this.el.enableDisplayMode('block');
19813 if (this.over === false && !this.parent()) {
19816 if (this.triggers === false) {
19821 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19822 var triggers = this.trigger ? this.trigger.split(' ') : [];
19823 Roo.each(triggers, function(trigger) {
19825 if (trigger == 'click') {
19826 on_el.on('click', this.toggle, this);
19827 } else if (trigger != 'manual') {
19828 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19829 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19831 on_el.on(eventIn ,this.enter, this);
19832 on_el.on(eventOut, this.leave, this);
19842 toggle : function () {
19843 this.hoverState == 'in' ? this.leave() : this.enter();
19846 enter : function () {
19848 clearTimeout(this.timeout);
19850 this.hoverState = 'in';
19852 if (!this.delay || !this.delay.show) {
19857 this.timeout = setTimeout(function () {
19858 if (_t.hoverState == 'in') {
19861 }, this.delay.show)
19864 leave : function() {
19865 clearTimeout(this.timeout);
19867 this.hoverState = 'out';
19869 if (!this.delay || !this.delay.hide) {
19874 this.timeout = setTimeout(function () {
19875 if (_t.hoverState == 'out') {
19878 }, this.delay.hide)
19882 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19883 * @param {string} (left|right|top|bottom) position
19885 show : function (on_el, placement)
19887 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19888 on_el = on_el || false; // default to false
19891 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19892 on_el = this.parent().el;
19893 } else if (this.over) {
19894 Roo.get(this.over);
19899 this.alignEl = Roo.get( on_el );
19902 this.render(document.body);
19908 if (this.title === false) {
19909 this.headerEl.hide();
19914 this.el.dom.style.display = 'block';
19917 if (this.alignEl) {
19918 this.updatePosition(this.placement, true);
19921 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19922 var es = this.el.getSize();
19923 var x = Roo.lib.Dom.getViewWidth()/2;
19924 var y = Roo.lib.Dom.getViewHeight()/2;
19925 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19930 //var arrow = this.el.select('.arrow',true).first();
19931 //arrow.set(align[2],
19933 this.el.addClass('in');
19937 this.hoverState = 'in';
19940 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19941 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19942 this.maskEl.dom.style.display = 'block';
19943 this.maskEl.addClass('show');
19945 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19947 this.fireEvent('show', this);
19951 * fire this manually after loading a grid in the table for example
19952 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19953 * @param {Boolean} try and move it if we cant get right position.
19955 updatePosition : function(placement, try_move)
19957 // allow for calling with no parameters
19958 placement = placement ? placement : this.placement;
19959 try_move = typeof(try_move) == 'undefined' ? true : try_move;
19961 this.el.removeClass([
19962 'fade','top','bottom', 'left', 'right','in',
19963 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19965 this.el.addClass(placement + ' bs-popover-' + placement);
19967 if (!this.alignEl ) {
19971 switch (placement) {
19973 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19974 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19975 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19976 //normal display... or moved up/down.
19977 this.el.setXY(offset);
19978 var xy = this.alignEl.getAnchorXY('tr', false);
19980 this.arrowEl.setXY(xy);
19983 // continue through...
19984 return this.updatePosition('left', false);
19988 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19989 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19990 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19991 //normal display... or moved up/down.
19992 this.el.setXY(offset);
19993 var xy = this.alignEl.getAnchorXY('tl', false);
19994 xy[0]-=10;xy[1]+=5; // << fix me
19995 this.arrowEl.setXY(xy);
19999 return this.updatePosition('right', false);
20002 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20003 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20004 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20005 //normal display... or moved up/down.
20006 this.el.setXY(offset);
20007 var xy = this.alignEl.getAnchorXY('t', false);
20008 xy[1]-=10; // << fix me
20009 this.arrowEl.setXY(xy);
20013 return this.updatePosition('bottom', false);
20016 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20017 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20018 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20019 //normal display... or moved up/down.
20020 this.el.setXY(offset);
20021 var xy = this.alignEl.getAnchorXY('b', false);
20022 xy[1]+=2; // << fix me
20023 this.arrowEl.setXY(xy);
20027 return this.updatePosition('top', false);
20038 this.el.setXY([0,0]);
20039 this.el.removeClass('in');
20041 this.hoverState = null;
20042 this.maskEl.hide(); // always..
20043 this.fireEvent('hide', this);
20049 Roo.apply(Roo.bootstrap.Popover, {
20052 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20053 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20054 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20055 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20060 clickHander : false,
20063 onMouseDown : function(e)
20065 if (!e.getTarget(".roo-popover")) {
20073 register : function(popup)
20075 if (!Roo.bootstrap.Popover.clickHandler) {
20076 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20078 // hide other popups.
20080 this.popups.push(popup);
20082 hideAll : function()
20084 this.popups.forEach(function(p) {
20092 * Card header - holder for the card header elements.
20097 * @class Roo.bootstrap.PopoverNav
20098 * @extends Roo.bootstrap.NavGroup
20099 * Bootstrap Popover header navigation class
20101 * Create a new Popover Header Navigation
20102 * @param {Object} config The config object
20105 Roo.bootstrap.PopoverNav = function(config){
20106 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20109 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20112 container_method : 'getPopoverHeader'
20130 * @class Roo.bootstrap.Progress
20131 * @extends Roo.bootstrap.Component
20132 * Bootstrap Progress class
20133 * @cfg {Boolean} striped striped of the progress bar
20134 * @cfg {Boolean} active animated of the progress bar
20138 * Create a new Progress
20139 * @param {Object} config The config object
20142 Roo.bootstrap.Progress = function(config){
20143 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20146 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20151 getAutoCreate : function(){
20159 cfg.cls += ' progress-striped';
20163 cfg.cls += ' active';
20182 * @class Roo.bootstrap.ProgressBar
20183 * @extends Roo.bootstrap.Component
20184 * Bootstrap ProgressBar class
20185 * @cfg {Number} aria_valuenow aria-value now
20186 * @cfg {Number} aria_valuemin aria-value min
20187 * @cfg {Number} aria_valuemax aria-value max
20188 * @cfg {String} label label for the progress bar
20189 * @cfg {String} panel (success | info | warning | danger )
20190 * @cfg {String} role role of the progress bar
20191 * @cfg {String} sr_only text
20195 * Create a new ProgressBar
20196 * @param {Object} config The config object
20199 Roo.bootstrap.ProgressBar = function(config){
20200 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20203 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20207 aria_valuemax : 100,
20213 getAutoCreate : function()
20218 cls: 'progress-bar',
20219 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20231 cfg.role = this.role;
20234 if(this.aria_valuenow){
20235 cfg['aria-valuenow'] = this.aria_valuenow;
20238 if(this.aria_valuemin){
20239 cfg['aria-valuemin'] = this.aria_valuemin;
20242 if(this.aria_valuemax){
20243 cfg['aria-valuemax'] = this.aria_valuemax;
20246 if(this.label && !this.sr_only){
20247 cfg.html = this.label;
20251 cfg.cls += ' progress-bar-' + this.panel;
20257 update : function(aria_valuenow)
20259 this.aria_valuenow = aria_valuenow;
20261 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20276 * @class Roo.bootstrap.TabGroup
20277 * @extends Roo.bootstrap.Column
20278 * Bootstrap Column class
20279 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20280 * @cfg {Boolean} carousel true to make the group behave like a carousel
20281 * @cfg {Boolean} bullets show bullets for the panels
20282 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20283 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20284 * @cfg {Boolean} showarrow (true|false) show arrow default true
20287 * Create a new TabGroup
20288 * @param {Object} config The config object
20291 Roo.bootstrap.TabGroup = function(config){
20292 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20294 this.navId = Roo.id();
20297 Roo.bootstrap.TabGroup.register(this);
20301 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20304 transition : false,
20309 slideOnTouch : false,
20312 getAutoCreate : function()
20314 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20316 cfg.cls += ' tab-content';
20318 if (this.carousel) {
20319 cfg.cls += ' carousel slide';
20322 cls : 'carousel-inner',
20326 if(this.bullets && !Roo.isTouch){
20329 cls : 'carousel-bullets',
20333 if(this.bullets_cls){
20334 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20341 cfg.cn[0].cn.push(bullets);
20344 if(this.showarrow){
20345 cfg.cn[0].cn.push({
20347 class : 'carousel-arrow',
20351 class : 'carousel-prev',
20355 class : 'fa fa-chevron-left'
20361 class : 'carousel-next',
20365 class : 'fa fa-chevron-right'
20378 initEvents: function()
20380 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20381 // this.el.on("touchstart", this.onTouchStart, this);
20384 if(this.autoslide){
20387 this.slideFn = window.setInterval(function() {
20388 _this.showPanelNext();
20392 if(this.showarrow){
20393 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20394 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20400 // onTouchStart : function(e, el, o)
20402 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20406 // this.showPanelNext();
20410 getChildContainer : function()
20412 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20416 * register a Navigation item
20417 * @param {Roo.bootstrap.NavItem} the navitem to add
20419 register : function(item)
20421 this.tabs.push( item);
20422 item.navId = this.navId; // not really needed..
20427 getActivePanel : function()
20430 Roo.each(this.tabs, function(t) {
20440 getPanelByName : function(n)
20443 Roo.each(this.tabs, function(t) {
20444 if (t.tabId == n) {
20452 indexOfPanel : function(p)
20455 Roo.each(this.tabs, function(t,i) {
20456 if (t.tabId == p.tabId) {
20465 * show a specific panel
20466 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20467 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20469 showPanel : function (pan)
20471 if(this.transition || typeof(pan) == 'undefined'){
20472 Roo.log("waiting for the transitionend");
20476 if (typeof(pan) == 'number') {
20477 pan = this.tabs[pan];
20480 if (typeof(pan) == 'string') {
20481 pan = this.getPanelByName(pan);
20484 var cur = this.getActivePanel();
20487 Roo.log('pan or acitve pan is undefined');
20491 if (pan.tabId == this.getActivePanel().tabId) {
20495 if (false === cur.fireEvent('beforedeactivate')) {
20499 if(this.bullets > 0 && !Roo.isTouch){
20500 this.setActiveBullet(this.indexOfPanel(pan));
20503 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20505 //class="carousel-item carousel-item-next carousel-item-left"
20507 this.transition = true;
20508 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20509 var lr = dir == 'next' ? 'left' : 'right';
20510 pan.el.addClass(dir); // or prev
20511 pan.el.addClass('carousel-item-' + dir); // or prev
20512 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20513 cur.el.addClass(lr); // or right
20514 pan.el.addClass(lr);
20515 cur.el.addClass('carousel-item-' +lr); // or right
20516 pan.el.addClass('carousel-item-' +lr);
20520 cur.el.on('transitionend', function() {
20521 Roo.log("trans end?");
20523 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20524 pan.setActive(true);
20526 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20527 cur.setActive(false);
20529 _this.transition = false;
20531 }, this, { single: true } );
20536 cur.setActive(false);
20537 pan.setActive(true);
20542 showPanelNext : function()
20544 var i = this.indexOfPanel(this.getActivePanel());
20546 if (i >= this.tabs.length - 1 && !this.autoslide) {
20550 if (i >= this.tabs.length - 1 && this.autoslide) {
20554 this.showPanel(this.tabs[i+1]);
20557 showPanelPrev : function()
20559 var i = this.indexOfPanel(this.getActivePanel());
20561 if (i < 1 && !this.autoslide) {
20565 if (i < 1 && this.autoslide) {
20566 i = this.tabs.length;
20569 this.showPanel(this.tabs[i-1]);
20573 addBullet: function()
20575 if(!this.bullets || Roo.isTouch){
20578 var ctr = this.el.select('.carousel-bullets',true).first();
20579 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20580 var bullet = ctr.createChild({
20581 cls : 'bullet bullet-' + i
20582 },ctr.dom.lastChild);
20587 bullet.on('click', (function(e, el, o, ii, t){
20589 e.preventDefault();
20591 this.showPanel(ii);
20593 if(this.autoslide && this.slideFn){
20594 clearInterval(this.slideFn);
20595 this.slideFn = window.setInterval(function() {
20596 _this.showPanelNext();
20600 }).createDelegate(this, [i, bullet], true));
20605 setActiveBullet : function(i)
20611 Roo.each(this.el.select('.bullet', true).elements, function(el){
20612 el.removeClass('selected');
20615 var bullet = this.el.select('.bullet-' + i, true).first();
20621 bullet.addClass('selected');
20632 Roo.apply(Roo.bootstrap.TabGroup, {
20636 * register a Navigation Group
20637 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20639 register : function(navgrp)
20641 this.groups[navgrp.navId] = navgrp;
20645 * fetch a Navigation Group based on the navigation ID
20646 * if one does not exist , it will get created.
20647 * @param {string} the navgroup to add
20648 * @returns {Roo.bootstrap.NavGroup} the navgroup
20650 get: function(navId) {
20651 if (typeof(this.groups[navId]) == 'undefined') {
20652 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20654 return this.groups[navId] ;
20669 * @class Roo.bootstrap.TabPanel
20670 * @extends Roo.bootstrap.Component
20671 * Bootstrap TabPanel class
20672 * @cfg {Boolean} active panel active
20673 * @cfg {String} html panel content
20674 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20675 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20676 * @cfg {String} href click to link..
20677 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20681 * Create a new TabPanel
20682 * @param {Object} config The config object
20685 Roo.bootstrap.TabPanel = function(config){
20686 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20690 * Fires when the active status changes
20691 * @param {Roo.bootstrap.TabPanel} this
20692 * @param {Boolean} state the new state
20697 * @event beforedeactivate
20698 * Fires before a tab is de-activated - can be used to do validation on a form.
20699 * @param {Roo.bootstrap.TabPanel} this
20700 * @return {Boolean} false if there is an error
20703 'beforedeactivate': true
20706 this.tabId = this.tabId || Roo.id();
20710 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20717 touchSlide : false,
20718 getAutoCreate : function(){
20723 // item is needed for carousel - not sure if it has any effect otherwise
20724 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20725 html: this.html || ''
20729 cfg.cls += ' active';
20733 cfg.tabId = this.tabId;
20741 initEvents: function()
20743 var p = this.parent();
20745 this.navId = this.navId || p.navId;
20747 if (typeof(this.navId) != 'undefined') {
20748 // not really needed.. but just in case.. parent should be a NavGroup.
20749 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20753 var i = tg.tabs.length - 1;
20755 if(this.active && tg.bullets > 0 && i < tg.bullets){
20756 tg.setActiveBullet(i);
20760 this.el.on('click', this.onClick, this);
20762 if(Roo.isTouch && this.touchSlide){
20763 this.el.on("touchstart", this.onTouchStart, this);
20764 this.el.on("touchmove", this.onTouchMove, this);
20765 this.el.on("touchend", this.onTouchEnd, this);
20770 onRender : function(ct, position)
20772 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20775 setActive : function(state)
20777 Roo.log("panel - set active " + this.tabId + "=" + state);
20779 this.active = state;
20781 this.el.removeClass('active');
20783 } else if (!this.el.hasClass('active')) {
20784 this.el.addClass('active');
20787 this.fireEvent('changed', this, state);
20790 onClick : function(e)
20792 e.preventDefault();
20794 if(!this.href.length){
20798 window.location.href = this.href;
20807 onTouchStart : function(e)
20809 this.swiping = false;
20811 this.startX = e.browserEvent.touches[0].clientX;
20812 this.startY = e.browserEvent.touches[0].clientY;
20815 onTouchMove : function(e)
20817 this.swiping = true;
20819 this.endX = e.browserEvent.touches[0].clientX;
20820 this.endY = e.browserEvent.touches[0].clientY;
20823 onTouchEnd : function(e)
20830 var tabGroup = this.parent();
20832 if(this.endX > this.startX){ // swiping right
20833 tabGroup.showPanelPrev();
20837 if(this.startX > this.endX){ // swiping left
20838 tabGroup.showPanelNext();
20857 * @class Roo.bootstrap.DateField
20858 * @extends Roo.bootstrap.Input
20859 * Bootstrap DateField class
20860 * @cfg {Number} weekStart default 0
20861 * @cfg {String} viewMode default empty, (months|years)
20862 * @cfg {String} minViewMode default empty, (months|years)
20863 * @cfg {Number} startDate default -Infinity
20864 * @cfg {Number} endDate default Infinity
20865 * @cfg {Boolean} todayHighlight default false
20866 * @cfg {Boolean} todayBtn default false
20867 * @cfg {Boolean} calendarWeeks default false
20868 * @cfg {Object} daysOfWeekDisabled default empty
20869 * @cfg {Boolean} singleMode default false (true | false)
20871 * @cfg {Boolean} keyboardNavigation default true
20872 * @cfg {String} language default en
20875 * Create a new DateField
20876 * @param {Object} config The config object
20879 Roo.bootstrap.DateField = function(config){
20880 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20884 * Fires when this field show.
20885 * @param {Roo.bootstrap.DateField} this
20886 * @param {Mixed} date The date value
20891 * Fires when this field hide.
20892 * @param {Roo.bootstrap.DateField} this
20893 * @param {Mixed} date The date value
20898 * Fires when select a date.
20899 * @param {Roo.bootstrap.DateField} this
20900 * @param {Mixed} date The date value
20904 * @event beforeselect
20905 * Fires when before select a date.
20906 * @param {Roo.bootstrap.DateField} this
20907 * @param {Mixed} date The date value
20909 beforeselect : true
20913 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20916 * @cfg {String} format
20917 * The default date format string which can be overriden for localization support. The format must be
20918 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20922 * @cfg {String} altFormats
20923 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20924 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20926 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20934 todayHighlight : false,
20940 keyboardNavigation: true,
20942 calendarWeeks: false,
20944 startDate: -Infinity,
20948 daysOfWeekDisabled: [],
20952 singleMode : false,
20954 UTCDate: function()
20956 return new Date(Date.UTC.apply(Date, arguments));
20959 UTCToday: function()
20961 var today = new Date();
20962 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20965 getDate: function() {
20966 var d = this.getUTCDate();
20967 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20970 getUTCDate: function() {
20974 setDate: function(d) {
20975 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20978 setUTCDate: function(d) {
20980 this.setValue(this.formatDate(this.date));
20983 onRender: function(ct, position)
20986 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20988 this.language = this.language || 'en';
20989 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20990 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20992 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20993 this.format = this.format || 'm/d/y';
20994 this.isInline = false;
20995 this.isInput = true;
20996 this.component = this.el.select('.add-on', true).first() || false;
20997 this.component = (this.component && this.component.length === 0) ? false : this.component;
20998 this.hasInput = this.component && this.inputEl().length;
21000 if (typeof(this.minViewMode === 'string')) {
21001 switch (this.minViewMode) {
21003 this.minViewMode = 1;
21006 this.minViewMode = 2;
21009 this.minViewMode = 0;
21014 if (typeof(this.viewMode === 'string')) {
21015 switch (this.viewMode) {
21028 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21030 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21032 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21034 this.picker().on('mousedown', this.onMousedown, this);
21035 this.picker().on('click', this.onClick, this);
21037 this.picker().addClass('datepicker-dropdown');
21039 this.startViewMode = this.viewMode;
21041 if(this.singleMode){
21042 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21043 v.setVisibilityMode(Roo.Element.DISPLAY);
21047 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21048 v.setStyle('width', '189px');
21052 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21053 if(!this.calendarWeeks){
21058 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21059 v.attr('colspan', function(i, val){
21060 return parseInt(val) + 1;
21065 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21067 this.setStartDate(this.startDate);
21068 this.setEndDate(this.endDate);
21070 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21077 if(this.isInline) {
21082 picker : function()
21084 return this.pickerEl;
21085 // return this.el.select('.datepicker', true).first();
21088 fillDow: function()
21090 var dowCnt = this.weekStart;
21099 if(this.calendarWeeks){
21107 while (dowCnt < this.weekStart + 7) {
21111 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21115 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21118 fillMonths: function()
21121 var months = this.picker().select('>.datepicker-months td', true).first();
21123 months.dom.innerHTML = '';
21129 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21132 months.createChild(month);
21139 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;
21141 if (this.date < this.startDate) {
21142 this.viewDate = new Date(this.startDate);
21143 } else if (this.date > this.endDate) {
21144 this.viewDate = new Date(this.endDate);
21146 this.viewDate = new Date(this.date);
21154 var d = new Date(this.viewDate),
21155 year = d.getUTCFullYear(),
21156 month = d.getUTCMonth(),
21157 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21158 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21159 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21160 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21161 currentDate = this.date && this.date.valueOf(),
21162 today = this.UTCToday();
21164 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21166 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21168 // this.picker.select('>tfoot th.today').
21169 // .text(dates[this.language].today)
21170 // .toggle(this.todayBtn !== false);
21172 this.updateNavArrows();
21175 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21177 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21179 prevMonth.setUTCDate(day);
21181 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21183 var nextMonth = new Date(prevMonth);
21185 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21187 nextMonth = nextMonth.valueOf();
21189 var fillMonths = false;
21191 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21193 while(prevMonth.valueOf() <= nextMonth) {
21196 if (prevMonth.getUTCDay() === this.weekStart) {
21198 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21206 if(this.calendarWeeks){
21207 // ISO 8601: First week contains first thursday.
21208 // ISO also states week starts on Monday, but we can be more abstract here.
21210 // Start of current week: based on weekstart/current date
21211 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21212 // Thursday of this week
21213 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21214 // First Thursday of year, year from thursday
21215 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21216 // Calendar week: ms between thursdays, div ms per day, div 7 days
21217 calWeek = (th - yth) / 864e5 / 7 + 1;
21219 fillMonths.cn.push({
21227 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21229 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21232 if (this.todayHighlight &&
21233 prevMonth.getUTCFullYear() == today.getFullYear() &&
21234 prevMonth.getUTCMonth() == today.getMonth() &&
21235 prevMonth.getUTCDate() == today.getDate()) {
21236 clsName += ' today';
21239 if (currentDate && prevMonth.valueOf() === currentDate) {
21240 clsName += ' active';
21243 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21244 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21245 clsName += ' disabled';
21248 fillMonths.cn.push({
21250 cls: 'day ' + clsName,
21251 html: prevMonth.getDate()
21254 prevMonth.setDate(prevMonth.getDate()+1);
21257 var currentYear = this.date && this.date.getUTCFullYear();
21258 var currentMonth = this.date && this.date.getUTCMonth();
21260 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21262 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21263 v.removeClass('active');
21265 if(currentYear === year && k === currentMonth){
21266 v.addClass('active');
21269 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21270 v.addClass('disabled');
21276 year = parseInt(year/10, 10) * 10;
21278 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21280 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21283 for (var i = -1; i < 11; i++) {
21284 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21286 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21294 showMode: function(dir)
21297 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21300 Roo.each(this.picker().select('>div',true).elements, function(v){
21301 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21304 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21309 if(this.isInline) {
21313 this.picker().removeClass(['bottom', 'top']);
21315 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21317 * place to the top of element!
21321 this.picker().addClass('top');
21322 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21327 this.picker().addClass('bottom');
21329 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21332 parseDate : function(value)
21334 if(!value || value instanceof Date){
21337 var v = Date.parseDate(value, this.format);
21338 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21339 v = Date.parseDate(value, 'Y-m-d');
21341 if(!v && this.altFormats){
21342 if(!this.altFormatsArray){
21343 this.altFormatsArray = this.altFormats.split("|");
21345 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21346 v = Date.parseDate(value, this.altFormatsArray[i]);
21352 formatDate : function(date, fmt)
21354 return (!date || !(date instanceof Date)) ?
21355 date : date.dateFormat(fmt || this.format);
21358 onFocus : function()
21360 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21364 onBlur : function()
21366 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21368 var d = this.inputEl().getValue();
21375 showPopup : function()
21377 this.picker().show();
21381 this.fireEvent('showpopup', this, this.date);
21384 hidePopup : function()
21386 if(this.isInline) {
21389 this.picker().hide();
21390 this.viewMode = this.startViewMode;
21393 this.fireEvent('hidepopup', this, this.date);
21397 onMousedown: function(e)
21399 e.stopPropagation();
21400 e.preventDefault();
21405 Roo.bootstrap.DateField.superclass.keyup.call(this);
21409 setValue: function(v)
21411 if(this.fireEvent('beforeselect', this, v) !== false){
21412 var d = new Date(this.parseDate(v) ).clearTime();
21414 if(isNaN(d.getTime())){
21415 this.date = this.viewDate = '';
21416 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21420 v = this.formatDate(d);
21422 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21424 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21428 this.fireEvent('select', this, this.date);
21432 getValue: function()
21434 return this.formatDate(this.date);
21437 fireKey: function(e)
21439 if (!this.picker().isVisible()){
21440 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21446 var dateChanged = false,
21448 newDate, newViewDate;
21453 e.preventDefault();
21457 if (!this.keyboardNavigation) {
21460 dir = e.keyCode == 37 ? -1 : 1;
21463 newDate = this.moveYear(this.date, dir);
21464 newViewDate = this.moveYear(this.viewDate, dir);
21465 } else if (e.shiftKey){
21466 newDate = this.moveMonth(this.date, dir);
21467 newViewDate = this.moveMonth(this.viewDate, dir);
21469 newDate = new Date(this.date);
21470 newDate.setUTCDate(this.date.getUTCDate() + dir);
21471 newViewDate = new Date(this.viewDate);
21472 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21474 if (this.dateWithinRange(newDate)){
21475 this.date = newDate;
21476 this.viewDate = newViewDate;
21477 this.setValue(this.formatDate(this.date));
21479 e.preventDefault();
21480 dateChanged = true;
21485 if (!this.keyboardNavigation) {
21488 dir = e.keyCode == 38 ? -1 : 1;
21490 newDate = this.moveYear(this.date, dir);
21491 newViewDate = this.moveYear(this.viewDate, dir);
21492 } else if (e.shiftKey){
21493 newDate = this.moveMonth(this.date, dir);
21494 newViewDate = this.moveMonth(this.viewDate, dir);
21496 newDate = new Date(this.date);
21497 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21498 newViewDate = new Date(this.viewDate);
21499 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21501 if (this.dateWithinRange(newDate)){
21502 this.date = newDate;
21503 this.viewDate = newViewDate;
21504 this.setValue(this.formatDate(this.date));
21506 e.preventDefault();
21507 dateChanged = true;
21511 this.setValue(this.formatDate(this.date));
21513 e.preventDefault();
21516 this.setValue(this.formatDate(this.date));
21530 onClick: function(e)
21532 e.stopPropagation();
21533 e.preventDefault();
21535 var target = e.getTarget();
21537 if(target.nodeName.toLowerCase() === 'i'){
21538 target = Roo.get(target).dom.parentNode;
21541 var nodeName = target.nodeName;
21542 var className = target.className;
21543 var html = target.innerHTML;
21544 //Roo.log(nodeName);
21546 switch(nodeName.toLowerCase()) {
21548 switch(className) {
21554 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21555 switch(this.viewMode){
21557 this.viewDate = this.moveMonth(this.viewDate, dir);
21561 this.viewDate = this.moveYear(this.viewDate, dir);
21567 var date = new Date();
21568 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21570 this.setValue(this.formatDate(this.date));
21577 if (className.indexOf('disabled') < 0) {
21578 this.viewDate.setUTCDate(1);
21579 if (className.indexOf('month') > -1) {
21580 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21582 var year = parseInt(html, 10) || 0;
21583 this.viewDate.setUTCFullYear(year);
21587 if(this.singleMode){
21588 this.setValue(this.formatDate(this.viewDate));
21599 //Roo.log(className);
21600 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21601 var day = parseInt(html, 10) || 1;
21602 var year = (this.viewDate || new Date()).getUTCFullYear(),
21603 month = (this.viewDate || new Date()).getUTCMonth();
21605 if (className.indexOf('old') > -1) {
21612 } else if (className.indexOf('new') > -1) {
21620 //Roo.log([year,month,day]);
21621 this.date = this.UTCDate(year, month, day,0,0,0,0);
21622 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21624 //Roo.log(this.formatDate(this.date));
21625 this.setValue(this.formatDate(this.date));
21632 setStartDate: function(startDate)
21634 this.startDate = startDate || -Infinity;
21635 if (this.startDate !== -Infinity) {
21636 this.startDate = this.parseDate(this.startDate);
21639 this.updateNavArrows();
21642 setEndDate: function(endDate)
21644 this.endDate = endDate || Infinity;
21645 if (this.endDate !== Infinity) {
21646 this.endDate = this.parseDate(this.endDate);
21649 this.updateNavArrows();
21652 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21654 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21655 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21656 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21658 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21659 return parseInt(d, 10);
21662 this.updateNavArrows();
21665 updateNavArrows: function()
21667 if(this.singleMode){
21671 var d = new Date(this.viewDate),
21672 year = d.getUTCFullYear(),
21673 month = d.getUTCMonth();
21675 Roo.each(this.picker().select('.prev', true).elements, function(v){
21677 switch (this.viewMode) {
21680 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21686 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21693 Roo.each(this.picker().select('.next', true).elements, function(v){
21695 switch (this.viewMode) {
21698 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21704 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21712 moveMonth: function(date, dir)
21717 var new_date = new Date(date.valueOf()),
21718 day = new_date.getUTCDate(),
21719 month = new_date.getUTCMonth(),
21720 mag = Math.abs(dir),
21722 dir = dir > 0 ? 1 : -1;
21725 // If going back one month, make sure month is not current month
21726 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21728 return new_date.getUTCMonth() == month;
21730 // If going forward one month, make sure month is as expected
21731 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21733 return new_date.getUTCMonth() != new_month;
21735 new_month = month + dir;
21736 new_date.setUTCMonth(new_month);
21737 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21738 if (new_month < 0 || new_month > 11) {
21739 new_month = (new_month + 12) % 12;
21742 // For magnitudes >1, move one month at a time...
21743 for (var i=0; i<mag; i++) {
21744 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21745 new_date = this.moveMonth(new_date, dir);
21747 // ...then reset the day, keeping it in the new month
21748 new_month = new_date.getUTCMonth();
21749 new_date.setUTCDate(day);
21751 return new_month != new_date.getUTCMonth();
21754 // Common date-resetting loop -- if date is beyond end of month, make it
21757 new_date.setUTCDate(--day);
21758 new_date.setUTCMonth(new_month);
21763 moveYear: function(date, dir)
21765 return this.moveMonth(date, dir*12);
21768 dateWithinRange: function(date)
21770 return date >= this.startDate && date <= this.endDate;
21776 this.picker().remove();
21779 validateValue : function(value)
21781 if(this.getVisibilityEl().hasClass('hidden')){
21785 if(value.length < 1) {
21786 if(this.allowBlank){
21792 if(value.length < this.minLength){
21795 if(value.length > this.maxLength){
21799 var vt = Roo.form.VTypes;
21800 if(!vt[this.vtype](value, this)){
21804 if(typeof this.validator == "function"){
21805 var msg = this.validator(value);
21811 if(this.regex && !this.regex.test(value)){
21815 if(typeof(this.parseDate(value)) == 'undefined'){
21819 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21823 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21833 this.date = this.viewDate = '';
21835 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21840 Roo.apply(Roo.bootstrap.DateField, {
21851 html: '<i class="fa fa-arrow-left"/>'
21861 html: '<i class="fa fa-arrow-right"/>'
21903 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21904 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21905 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21906 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21907 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21920 navFnc: 'FullYear',
21925 navFnc: 'FullYear',
21930 Roo.apply(Roo.bootstrap.DateField, {
21934 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21938 cls: 'datepicker-days',
21942 cls: 'table-condensed',
21944 Roo.bootstrap.DateField.head,
21948 Roo.bootstrap.DateField.footer
21955 cls: 'datepicker-months',
21959 cls: 'table-condensed',
21961 Roo.bootstrap.DateField.head,
21962 Roo.bootstrap.DateField.content,
21963 Roo.bootstrap.DateField.footer
21970 cls: 'datepicker-years',
21974 cls: 'table-condensed',
21976 Roo.bootstrap.DateField.head,
21977 Roo.bootstrap.DateField.content,
21978 Roo.bootstrap.DateField.footer
21997 * @class Roo.bootstrap.TimeField
21998 * @extends Roo.bootstrap.Input
21999 * Bootstrap DateField class
22003 * Create a new TimeField
22004 * @param {Object} config The config object
22007 Roo.bootstrap.TimeField = function(config){
22008 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22012 * Fires when this field show.
22013 * @param {Roo.bootstrap.DateField} thisthis
22014 * @param {Mixed} date The date value
22019 * Fires when this field hide.
22020 * @param {Roo.bootstrap.DateField} this
22021 * @param {Mixed} date The date value
22026 * Fires when select a date.
22027 * @param {Roo.bootstrap.DateField} this
22028 * @param {Mixed} date The date value
22034 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22037 * @cfg {String} format
22038 * The default time format string which can be overriden for localization support. The format must be
22039 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22043 getAutoCreate : function()
22045 this.after = '<i class="fa far fa-clock"></i>';
22046 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22050 onRender: function(ct, position)
22053 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22055 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22057 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22059 this.pop = this.picker().select('>.datepicker-time',true).first();
22060 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22062 this.picker().on('mousedown', this.onMousedown, this);
22063 this.picker().on('click', this.onClick, this);
22065 this.picker().addClass('datepicker-dropdown');
22070 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22071 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22072 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22073 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22074 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22075 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22079 fireKey: function(e){
22080 if (!this.picker().isVisible()){
22081 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22087 e.preventDefault();
22095 this.onTogglePeriod();
22098 this.onIncrementMinutes();
22101 this.onDecrementMinutes();
22110 onClick: function(e) {
22111 e.stopPropagation();
22112 e.preventDefault();
22115 picker : function()
22117 return this.pickerEl;
22120 fillTime: function()
22122 var time = this.pop.select('tbody', true).first();
22124 time.dom.innerHTML = '';
22139 cls: 'hours-up fa fas fa-chevron-up'
22159 cls: 'minutes-up fa fas fa-chevron-up'
22180 cls: 'timepicker-hour',
22195 cls: 'timepicker-minute',
22210 cls: 'btn btn-primary period',
22232 cls: 'hours-down fa fas fa-chevron-down'
22252 cls: 'minutes-down fa fas fa-chevron-down'
22270 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22277 var hours = this.time.getHours();
22278 var minutes = this.time.getMinutes();
22291 hours = hours - 12;
22295 hours = '0' + hours;
22299 minutes = '0' + minutes;
22302 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22303 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22304 this.pop.select('button', true).first().dom.innerHTML = period;
22310 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22312 var cls = ['bottom'];
22314 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22321 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22325 //this.picker().setXY(20000,20000);
22326 this.picker().addClass(cls.join('-'));
22330 Roo.each(cls, function(c){
22335 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22336 //_this.picker().setTop(_this.inputEl().getHeight());
22340 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22342 //_this.picker().setTop(0 - _this.picker().getHeight());
22347 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22351 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22359 onFocus : function()
22361 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22365 onBlur : function()
22367 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22373 this.picker().show();
22378 this.fireEvent('show', this, this.date);
22383 this.picker().hide();
22386 this.fireEvent('hide', this, this.date);
22389 setTime : function()
22392 this.setValue(this.time.format(this.format));
22394 this.fireEvent('select', this, this.date);
22399 onMousedown: function(e){
22400 e.stopPropagation();
22401 e.preventDefault();
22404 onIncrementHours: function()
22406 Roo.log('onIncrementHours');
22407 this.time = this.time.add(Date.HOUR, 1);
22412 onDecrementHours: function()
22414 Roo.log('onDecrementHours');
22415 this.time = this.time.add(Date.HOUR, -1);
22419 onIncrementMinutes: function()
22421 Roo.log('onIncrementMinutes');
22422 this.time = this.time.add(Date.MINUTE, 1);
22426 onDecrementMinutes: function()
22428 Roo.log('onDecrementMinutes');
22429 this.time = this.time.add(Date.MINUTE, -1);
22433 onTogglePeriod: function()
22435 Roo.log('onTogglePeriod');
22436 this.time = this.time.add(Date.HOUR, 12);
22444 Roo.apply(Roo.bootstrap.TimeField, {
22448 cls: 'datepicker dropdown-menu',
22452 cls: 'datepicker-time',
22456 cls: 'table-condensed',
22485 cls: 'btn btn-info ok',
22513 * @class Roo.bootstrap.MonthField
22514 * @extends Roo.bootstrap.Input
22515 * Bootstrap MonthField class
22517 * @cfg {String} language default en
22520 * Create a new MonthField
22521 * @param {Object} config The config object
22524 Roo.bootstrap.MonthField = function(config){
22525 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22530 * Fires when this field show.
22531 * @param {Roo.bootstrap.MonthField} this
22532 * @param {Mixed} date The date value
22537 * Fires when this field hide.
22538 * @param {Roo.bootstrap.MonthField} this
22539 * @param {Mixed} date The date value
22544 * Fires when select a date.
22545 * @param {Roo.bootstrap.MonthField} this
22546 * @param {String} oldvalue The old value
22547 * @param {String} newvalue The new value
22553 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22555 onRender: function(ct, position)
22558 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22560 this.language = this.language || 'en';
22561 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22562 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22564 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22565 this.isInline = false;
22566 this.isInput = true;
22567 this.component = this.el.select('.add-on', true).first() || false;
22568 this.component = (this.component && this.component.length === 0) ? false : this.component;
22569 this.hasInput = this.component && this.inputEL().length;
22571 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22573 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22575 this.picker().on('mousedown', this.onMousedown, this);
22576 this.picker().on('click', this.onClick, this);
22578 this.picker().addClass('datepicker-dropdown');
22580 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22581 v.setStyle('width', '189px');
22588 if(this.isInline) {
22594 setValue: function(v, suppressEvent)
22596 var o = this.getValue();
22598 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22602 if(suppressEvent !== true){
22603 this.fireEvent('select', this, o, v);
22608 getValue: function()
22613 onClick: function(e)
22615 e.stopPropagation();
22616 e.preventDefault();
22618 var target = e.getTarget();
22620 if(target.nodeName.toLowerCase() === 'i'){
22621 target = Roo.get(target).dom.parentNode;
22624 var nodeName = target.nodeName;
22625 var className = target.className;
22626 var html = target.innerHTML;
22628 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22632 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22634 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22640 picker : function()
22642 return this.pickerEl;
22645 fillMonths: function()
22648 var months = this.picker().select('>.datepicker-months td', true).first();
22650 months.dom.innerHTML = '';
22656 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22659 months.createChild(month);
22668 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22669 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22672 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22673 e.removeClass('active');
22675 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22676 e.addClass('active');
22683 if(this.isInline) {
22687 this.picker().removeClass(['bottom', 'top']);
22689 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22691 * place to the top of element!
22695 this.picker().addClass('top');
22696 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22701 this.picker().addClass('bottom');
22703 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22706 onFocus : function()
22708 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22712 onBlur : function()
22714 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22716 var d = this.inputEl().getValue();
22725 this.picker().show();
22726 this.picker().select('>.datepicker-months', true).first().show();
22730 this.fireEvent('show', this, this.date);
22735 if(this.isInline) {
22738 this.picker().hide();
22739 this.fireEvent('hide', this, this.date);
22743 onMousedown: function(e)
22745 e.stopPropagation();
22746 e.preventDefault();
22751 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22755 fireKey: function(e)
22757 if (!this.picker().isVisible()){
22758 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22769 e.preventDefault();
22773 dir = e.keyCode == 37 ? -1 : 1;
22775 this.vIndex = this.vIndex + dir;
22777 if(this.vIndex < 0){
22781 if(this.vIndex > 11){
22785 if(isNaN(this.vIndex)){
22789 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22795 dir = e.keyCode == 38 ? -1 : 1;
22797 this.vIndex = this.vIndex + dir * 4;
22799 if(this.vIndex < 0){
22803 if(this.vIndex > 11){
22807 if(isNaN(this.vIndex)){
22811 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22816 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22817 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22821 e.preventDefault();
22824 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22825 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22841 this.picker().remove();
22846 Roo.apply(Roo.bootstrap.MonthField, {
22865 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22866 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22871 Roo.apply(Roo.bootstrap.MonthField, {
22875 cls: 'datepicker dropdown-menu roo-dynamic',
22879 cls: 'datepicker-months',
22883 cls: 'table-condensed',
22885 Roo.bootstrap.DateField.content
22905 * @class Roo.bootstrap.CheckBox
22906 * @extends Roo.bootstrap.Input
22907 * Bootstrap CheckBox class
22909 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22910 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22911 * @cfg {String} boxLabel The text that appears beside the checkbox
22912 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22913 * @cfg {Boolean} checked initnal the element
22914 * @cfg {Boolean} inline inline the element (default false)
22915 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22916 * @cfg {String} tooltip label tooltip
22919 * Create a new CheckBox
22920 * @param {Object} config The config object
22923 Roo.bootstrap.CheckBox = function(config){
22924 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22929 * Fires when the element is checked or unchecked.
22930 * @param {Roo.bootstrap.CheckBox} this This input
22931 * @param {Boolean} checked The new checked value
22936 * Fires when the element is click.
22937 * @param {Roo.bootstrap.CheckBox} this This input
22944 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22946 inputType: 'checkbox',
22955 // checkbox success does not make any sense really..
22960 getAutoCreate : function()
22962 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22968 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22971 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22977 type : this.inputType,
22978 value : this.inputValue,
22979 cls : 'roo-' + this.inputType, //'form-box',
22980 placeholder : this.placeholder || ''
22984 if(this.inputType != 'radio'){
22988 cls : 'roo-hidden-value',
22989 value : this.checked ? this.inputValue : this.valueOff
22994 if (this.weight) { // Validity check?
22995 cfg.cls += " " + this.inputType + "-" + this.weight;
22998 if (this.disabled) {
22999 input.disabled=true;
23003 input.checked = this.checked;
23008 input.name = this.name;
23010 if(this.inputType != 'radio'){
23011 hidden.name = this.name;
23012 input.name = '_hidden_' + this.name;
23017 input.cls += ' input-' + this.size;
23022 ['xs','sm','md','lg'].map(function(size){
23023 if (settings[size]) {
23024 cfg.cls += ' col-' + size + '-' + settings[size];
23028 var inputblock = input;
23030 if (this.before || this.after) {
23033 cls : 'input-group',
23038 inputblock.cn.push({
23040 cls : 'input-group-addon',
23045 inputblock.cn.push(input);
23047 if(this.inputType != 'radio'){
23048 inputblock.cn.push(hidden);
23052 inputblock.cn.push({
23054 cls : 'input-group-addon',
23060 var boxLabelCfg = false;
23066 //'for': id, // box label is handled by onclick - so no for...
23068 html: this.boxLabel
23071 boxLabelCfg.tooltip = this.tooltip;
23077 if (align ==='left' && this.fieldLabel.length) {
23078 // Roo.log("left and has label");
23083 cls : 'control-label',
23084 html : this.fieldLabel
23095 cfg.cn[1].cn.push(boxLabelCfg);
23098 if(this.labelWidth > 12){
23099 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23102 if(this.labelWidth < 13 && this.labelmd == 0){
23103 this.labelmd = this.labelWidth;
23106 if(this.labellg > 0){
23107 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23108 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23111 if(this.labelmd > 0){
23112 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23113 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23116 if(this.labelsm > 0){
23117 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23118 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23121 if(this.labelxs > 0){
23122 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23123 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23126 } else if ( this.fieldLabel.length) {
23127 // Roo.log(" label");
23131 tag: this.boxLabel ? 'span' : 'label',
23133 cls: 'control-label box-input-label',
23134 //cls : 'input-group-addon',
23135 html : this.fieldLabel
23142 cfg.cn.push(boxLabelCfg);
23147 // Roo.log(" no label && no align");
23148 cfg.cn = [ inputblock ] ;
23150 cfg.cn.push(boxLabelCfg);
23158 if(this.inputType != 'radio'){
23159 cfg.cn.push(hidden);
23167 * return the real input element.
23169 inputEl: function ()
23171 return this.el.select('input.roo-' + this.inputType,true).first();
23173 hiddenEl: function ()
23175 return this.el.select('input.roo-hidden-value',true).first();
23178 labelEl: function()
23180 return this.el.select('label.control-label',true).first();
23182 /* depricated... */
23186 return this.labelEl();
23189 boxLabelEl: function()
23191 return this.el.select('label.box-label',true).first();
23194 initEvents : function()
23196 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23198 this.inputEl().on('click', this.onClick, this);
23200 if (this.boxLabel) {
23201 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23204 this.startValue = this.getValue();
23207 Roo.bootstrap.CheckBox.register(this);
23211 onClick : function(e)
23213 if(this.fireEvent('click', this, e) !== false){
23214 this.setChecked(!this.checked);
23219 setChecked : function(state,suppressEvent)
23221 this.startValue = this.getValue();
23223 if(this.inputType == 'radio'){
23225 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23226 e.dom.checked = false;
23229 this.inputEl().dom.checked = true;
23231 this.inputEl().dom.value = this.inputValue;
23233 if(suppressEvent !== true){
23234 this.fireEvent('check', this, true);
23242 this.checked = state;
23244 this.inputEl().dom.checked = state;
23247 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23249 if(suppressEvent !== true){
23250 this.fireEvent('check', this, state);
23256 getValue : function()
23258 if(this.inputType == 'radio'){
23259 return this.getGroupValue();
23262 return this.hiddenEl().dom.value;
23266 getGroupValue : function()
23268 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23272 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23275 setValue : function(v,suppressEvent)
23277 if(this.inputType == 'radio'){
23278 this.setGroupValue(v, suppressEvent);
23282 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23287 setGroupValue : function(v, suppressEvent)
23289 this.startValue = this.getValue();
23291 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23292 e.dom.checked = false;
23294 if(e.dom.value == v){
23295 e.dom.checked = true;
23299 if(suppressEvent !== true){
23300 this.fireEvent('check', this, true);
23308 validate : function()
23310 if(this.getVisibilityEl().hasClass('hidden')){
23316 (this.inputType == 'radio' && this.validateRadio()) ||
23317 (this.inputType == 'checkbox' && this.validateCheckbox())
23323 this.markInvalid();
23327 validateRadio : function()
23329 if(this.getVisibilityEl().hasClass('hidden')){
23333 if(this.allowBlank){
23339 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23340 if(!e.dom.checked){
23352 validateCheckbox : function()
23355 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23356 //return (this.getValue() == this.inputValue) ? true : false;
23359 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23367 for(var i in group){
23368 if(group[i].el.isVisible(true)){
23376 for(var i in group){
23381 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23388 * Mark this field as valid
23390 markValid : function()
23394 this.fireEvent('valid', this);
23396 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23399 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23406 if(this.inputType == 'radio'){
23407 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23408 var fg = e.findParent('.form-group', false, true);
23409 if (Roo.bootstrap.version == 3) {
23410 fg.removeClass([_this.invalidClass, _this.validClass]);
23411 fg.addClass(_this.validClass);
23413 fg.removeClass(['is-valid', 'is-invalid']);
23414 fg.addClass('is-valid');
23422 var fg = this.el.findParent('.form-group', false, true);
23423 if (Roo.bootstrap.version == 3) {
23424 fg.removeClass([this.invalidClass, this.validClass]);
23425 fg.addClass(this.validClass);
23427 fg.removeClass(['is-valid', 'is-invalid']);
23428 fg.addClass('is-valid');
23433 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23439 for(var i in group){
23440 var fg = group[i].el.findParent('.form-group', false, true);
23441 if (Roo.bootstrap.version == 3) {
23442 fg.removeClass([this.invalidClass, this.validClass]);
23443 fg.addClass(this.validClass);
23445 fg.removeClass(['is-valid', 'is-invalid']);
23446 fg.addClass('is-valid');
23452 * Mark this field as invalid
23453 * @param {String} msg The validation message
23455 markInvalid : function(msg)
23457 if(this.allowBlank){
23463 this.fireEvent('invalid', this, msg);
23465 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23468 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23472 label.markInvalid();
23475 if(this.inputType == 'radio'){
23477 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23478 var fg = e.findParent('.form-group', false, true);
23479 if (Roo.bootstrap.version == 3) {
23480 fg.removeClass([_this.invalidClass, _this.validClass]);
23481 fg.addClass(_this.invalidClass);
23483 fg.removeClass(['is-invalid', 'is-valid']);
23484 fg.addClass('is-invalid');
23492 var fg = this.el.findParent('.form-group', false, true);
23493 if (Roo.bootstrap.version == 3) {
23494 fg.removeClass([_this.invalidClass, _this.validClass]);
23495 fg.addClass(_this.invalidClass);
23497 fg.removeClass(['is-invalid', 'is-valid']);
23498 fg.addClass('is-invalid');
23503 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23509 for(var i in group){
23510 var fg = group[i].el.findParent('.form-group', false, true);
23511 if (Roo.bootstrap.version == 3) {
23512 fg.removeClass([_this.invalidClass, _this.validClass]);
23513 fg.addClass(_this.invalidClass);
23515 fg.removeClass(['is-invalid', 'is-valid']);
23516 fg.addClass('is-invalid');
23522 clearInvalid : function()
23524 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23526 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23528 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23530 if (label && label.iconEl) {
23531 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23532 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23536 disable : function()
23538 if(this.inputType != 'radio'){
23539 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23546 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23547 _this.getActionEl().addClass(this.disabledClass);
23548 e.dom.disabled = true;
23552 this.disabled = true;
23553 this.fireEvent("disable", this);
23557 enable : function()
23559 if(this.inputType != 'radio'){
23560 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23567 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23568 _this.getActionEl().removeClass(this.disabledClass);
23569 e.dom.disabled = false;
23573 this.disabled = false;
23574 this.fireEvent("enable", this);
23578 setBoxLabel : function(v)
23583 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23589 Roo.apply(Roo.bootstrap.CheckBox, {
23594 * register a CheckBox Group
23595 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23597 register : function(checkbox)
23599 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23600 this.groups[checkbox.groupId] = {};
23603 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23607 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23611 * fetch a CheckBox Group based on the group ID
23612 * @param {string} the group ID
23613 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23615 get: function(groupId) {
23616 if (typeof(this.groups[groupId]) == 'undefined') {
23620 return this.groups[groupId] ;
23633 * @class Roo.bootstrap.Radio
23634 * @extends Roo.bootstrap.Component
23635 * Bootstrap Radio class
23636 * @cfg {String} boxLabel - the label associated
23637 * @cfg {String} value - the value of radio
23640 * Create a new Radio
23641 * @param {Object} config The config object
23643 Roo.bootstrap.Radio = function(config){
23644 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23648 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23654 getAutoCreate : function()
23658 cls : 'form-group radio',
23663 html : this.boxLabel
23671 initEvents : function()
23673 this.parent().register(this);
23675 this.el.on('click', this.onClick, this);
23679 onClick : function(e)
23681 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23682 this.setChecked(true);
23686 setChecked : function(state, suppressEvent)
23688 this.parent().setValue(this.value, suppressEvent);
23692 setBoxLabel : function(v)
23697 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23712 * @class Roo.bootstrap.SecurePass
23713 * @extends Roo.bootstrap.Input
23714 * Bootstrap SecurePass class
23718 * Create a new SecurePass
23719 * @param {Object} config The config object
23722 Roo.bootstrap.SecurePass = function (config) {
23723 // these go here, so the translation tool can replace them..
23725 PwdEmpty: "Please type a password, and then retype it to confirm.",
23726 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23727 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23728 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23729 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23730 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23731 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23732 TooWeak: "Your password is Too Weak."
23734 this.meterLabel = "Password strength:";
23735 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23736 this.meterClass = [
23737 "roo-password-meter-tooweak",
23738 "roo-password-meter-weak",
23739 "roo-password-meter-medium",
23740 "roo-password-meter-strong",
23741 "roo-password-meter-grey"
23746 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23749 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23751 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23753 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23754 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23755 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23756 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23757 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23758 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23759 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23769 * @cfg {String/Object} Label for the strength meter (defaults to
23770 * 'Password strength:')
23775 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23776 * ['Weak', 'Medium', 'Strong'])
23779 pwdStrengths: false,
23792 initEvents: function ()
23794 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23796 if (this.el.is('input[type=password]') && Roo.isSafari) {
23797 this.el.on('keydown', this.SafariOnKeyDown, this);
23800 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23803 onRender: function (ct, position)
23805 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23806 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23807 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23809 this.trigger.createChild({
23814 cls: 'roo-password-meter-grey col-xs-12',
23817 //width: this.meterWidth + 'px'
23821 cls: 'roo-password-meter-text'
23827 if (this.hideTrigger) {
23828 this.trigger.setDisplayed(false);
23830 this.setSize(this.width || '', this.height || '');
23833 onDestroy: function ()
23835 if (this.trigger) {
23836 this.trigger.removeAllListeners();
23837 this.trigger.remove();
23840 this.wrap.remove();
23842 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23845 checkStrength: function ()
23847 var pwd = this.inputEl().getValue();
23848 if (pwd == this._lastPwd) {
23853 if (this.ClientSideStrongPassword(pwd)) {
23855 } else if (this.ClientSideMediumPassword(pwd)) {
23857 } else if (this.ClientSideWeakPassword(pwd)) {
23863 Roo.log('strength1: ' + strength);
23865 //var pm = this.trigger.child('div/div/div').dom;
23866 var pm = this.trigger.child('div/div');
23867 pm.removeClass(this.meterClass);
23868 pm.addClass(this.meterClass[strength]);
23871 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23873 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23875 this._lastPwd = pwd;
23879 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23881 this._lastPwd = '';
23883 var pm = this.trigger.child('div/div');
23884 pm.removeClass(this.meterClass);
23885 pm.addClass('roo-password-meter-grey');
23888 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23891 this.inputEl().dom.type='password';
23894 validateValue: function (value)
23896 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23899 if (value.length == 0) {
23900 if (this.allowBlank) {
23901 this.clearInvalid();
23905 this.markInvalid(this.errors.PwdEmpty);
23906 this.errorMsg = this.errors.PwdEmpty;
23914 if (!value.match(/[\x21-\x7e]+/)) {
23915 this.markInvalid(this.errors.PwdBadChar);
23916 this.errorMsg = this.errors.PwdBadChar;
23919 if (value.length < 6) {
23920 this.markInvalid(this.errors.PwdShort);
23921 this.errorMsg = this.errors.PwdShort;
23924 if (value.length > 16) {
23925 this.markInvalid(this.errors.PwdLong);
23926 this.errorMsg = this.errors.PwdLong;
23930 if (this.ClientSideStrongPassword(value)) {
23932 } else if (this.ClientSideMediumPassword(value)) {
23934 } else if (this.ClientSideWeakPassword(value)) {
23941 if (strength < 2) {
23942 //this.markInvalid(this.errors.TooWeak);
23943 this.errorMsg = this.errors.TooWeak;
23948 console.log('strength2: ' + strength);
23950 //var pm = this.trigger.child('div/div/div').dom;
23952 var pm = this.trigger.child('div/div');
23953 pm.removeClass(this.meterClass);
23954 pm.addClass(this.meterClass[strength]);
23956 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23958 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23960 this.errorMsg = '';
23964 CharacterSetChecks: function (type)
23967 this.fResult = false;
23970 isctype: function (character, type)
23973 case this.kCapitalLetter:
23974 if (character >= 'A' && character <= 'Z') {
23979 case this.kSmallLetter:
23980 if (character >= 'a' && character <= 'z') {
23986 if (character >= '0' && character <= '9') {
23991 case this.kPunctuation:
23992 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24003 IsLongEnough: function (pwd, size)
24005 return !(pwd == null || isNaN(size) || pwd.length < size);
24008 SpansEnoughCharacterSets: function (word, nb)
24010 if (!this.IsLongEnough(word, nb))
24015 var characterSetChecks = new Array(
24016 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24017 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24020 for (var index = 0; index < word.length; ++index) {
24021 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24022 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24023 characterSetChecks[nCharSet].fResult = true;
24030 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24031 if (characterSetChecks[nCharSet].fResult) {
24036 if (nCharSets < nb) {
24042 ClientSideStrongPassword: function (pwd)
24044 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24047 ClientSideMediumPassword: function (pwd)
24049 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24052 ClientSideWeakPassword: function (pwd)
24054 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24057 })//<script type="text/javascript">
24060 * Based Ext JS Library 1.1.1
24061 * Copyright(c) 2006-2007, Ext JS, LLC.
24067 * @class Roo.HtmlEditorCore
24068 * @extends Roo.Component
24069 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24071 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24074 Roo.HtmlEditorCore = function(config){
24077 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24082 * @event initialize
24083 * Fires when the editor is fully initialized (including the iframe)
24084 * @param {Roo.HtmlEditorCore} this
24089 * Fires when the editor is first receives the focus. Any insertion must wait
24090 * until after this event.
24091 * @param {Roo.HtmlEditorCore} this
24095 * @event beforesync
24096 * Fires before the textarea is updated with content from the editor iframe. Return false
24097 * to cancel the sync.
24098 * @param {Roo.HtmlEditorCore} this
24099 * @param {String} html
24103 * @event beforepush
24104 * Fires before the iframe editor is updated with content from the textarea. Return false
24105 * to cancel the push.
24106 * @param {Roo.HtmlEditorCore} this
24107 * @param {String} html
24112 * Fires when the textarea is updated with content from the editor iframe.
24113 * @param {Roo.HtmlEditorCore} this
24114 * @param {String} html
24119 * Fires when the iframe editor is updated with content from the textarea.
24120 * @param {Roo.HtmlEditorCore} this
24121 * @param {String} html
24126 * @event editorevent
24127 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24128 * @param {Roo.HtmlEditorCore} this
24134 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24136 // defaults : white / black...
24137 this.applyBlacklists();
24144 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24148 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24154 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24159 * @cfg {Number} height (in pixels)
24163 * @cfg {Number} width (in pixels)
24168 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24171 stylesheets: false,
24176 // private properties
24177 validationEvent : false,
24179 initialized : false,
24181 sourceEditMode : false,
24182 onFocus : Roo.emptyFn,
24184 hideMode:'offsets',
24188 // blacklist + whitelisted elements..
24195 * Protected method that will not generally be called directly. It
24196 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24197 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24199 getDocMarkup : function(){
24203 // inherit styels from page...??
24204 if (this.stylesheets === false) {
24206 Roo.get(document.head).select('style').each(function(node) {
24207 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24210 Roo.get(document.head).select('link').each(function(node) {
24211 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24214 } else if (!this.stylesheets.length) {
24216 st = '<style type="text/css">' +
24217 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24220 for (var i in this.stylesheets) {
24221 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24226 st += '<style type="text/css">' +
24227 'IMG { cursor: pointer } ' +
24230 var cls = 'roo-htmleditor-body';
24232 if(this.bodyCls.length){
24233 cls += ' ' + this.bodyCls;
24236 return '<html><head>' + st +
24237 //<style type="text/css">' +
24238 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24240 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24244 onRender : function(ct, position)
24247 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24248 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24251 this.el.dom.style.border = '0 none';
24252 this.el.dom.setAttribute('tabIndex', -1);
24253 this.el.addClass('x-hidden hide');
24257 if(Roo.isIE){ // fix IE 1px bogus margin
24258 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24262 this.frameId = Roo.id();
24266 var iframe = this.owner.wrap.createChild({
24268 cls: 'form-control', // bootstrap..
24270 name: this.frameId,
24271 frameBorder : 'no',
24272 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24277 this.iframe = iframe.dom;
24279 this.assignDocWin();
24281 this.doc.designMode = 'on';
24284 this.doc.write(this.getDocMarkup());
24288 var task = { // must defer to wait for browser to be ready
24290 //console.log("run task?" + this.doc.readyState);
24291 this.assignDocWin();
24292 if(this.doc.body || this.doc.readyState == 'complete'){
24294 this.doc.designMode="on";
24298 Roo.TaskMgr.stop(task);
24299 this.initEditor.defer(10, this);
24306 Roo.TaskMgr.start(task);
24311 onResize : function(w, h)
24313 Roo.log('resize: ' +w + ',' + h );
24314 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24318 if(typeof w == 'number'){
24320 this.iframe.style.width = w + 'px';
24322 if(typeof h == 'number'){
24324 this.iframe.style.height = h + 'px';
24326 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24333 * Toggles the editor between standard and source edit mode.
24334 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24336 toggleSourceEdit : function(sourceEditMode){
24338 this.sourceEditMode = sourceEditMode === true;
24340 if(this.sourceEditMode){
24342 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24345 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24346 //this.iframe.className = '';
24349 //this.setSize(this.owner.wrap.getSize());
24350 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24357 * Protected method that will not generally be called directly. If you need/want
24358 * custom HTML cleanup, this is the method you should override.
24359 * @param {String} html The HTML to be cleaned
24360 * return {String} The cleaned HTML
24362 cleanHtml : function(html){
24363 html = String(html);
24364 if(html.length > 5){
24365 if(Roo.isSafari){ // strip safari nonsense
24366 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24369 if(html == ' '){
24376 * HTML Editor -> Textarea
24377 * Protected method that will not generally be called directly. Syncs the contents
24378 * of the editor iframe with the textarea.
24380 syncValue : function(){
24381 if(this.initialized){
24382 var bd = (this.doc.body || this.doc.documentElement);
24383 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24384 var html = bd.innerHTML;
24386 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24387 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24389 html = '<div style="'+m[0]+'">' + html + '</div>';
24392 html = this.cleanHtml(html);
24393 // fix up the special chars.. normaly like back quotes in word...
24394 // however we do not want to do this with chinese..
24395 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24397 var cc = match.charCodeAt();
24399 // Get the character value, handling surrogate pairs
24400 if (match.length == 2) {
24401 // It's a surrogate pair, calculate the Unicode code point
24402 var high = match.charCodeAt(0) - 0xD800;
24403 var low = match.charCodeAt(1) - 0xDC00;
24404 cc = (high * 0x400) + low + 0x10000;
24406 (cc >= 0x4E00 && cc < 0xA000 ) ||
24407 (cc >= 0x3400 && cc < 0x4E00 ) ||
24408 (cc >= 0xf900 && cc < 0xfb00 )
24413 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24414 return "&#" + cc + ";";
24421 if(this.owner.fireEvent('beforesync', this, html) !== false){
24422 this.el.dom.value = html;
24423 this.owner.fireEvent('sync', this, html);
24429 * Protected method that will not generally be called directly. Pushes the value of the textarea
24430 * into the iframe editor.
24432 pushValue : function(){
24433 if(this.initialized){
24434 var v = this.el.dom.value.trim();
24436 // if(v.length < 1){
24440 if(this.owner.fireEvent('beforepush', this, v) !== false){
24441 var d = (this.doc.body || this.doc.documentElement);
24443 this.cleanUpPaste();
24444 this.el.dom.value = d.innerHTML;
24445 this.owner.fireEvent('push', this, v);
24451 deferFocus : function(){
24452 this.focus.defer(10, this);
24456 focus : function(){
24457 if(this.win && !this.sourceEditMode){
24464 assignDocWin: function()
24466 var iframe = this.iframe;
24469 this.doc = iframe.contentWindow.document;
24470 this.win = iframe.contentWindow;
24472 // if (!Roo.get(this.frameId)) {
24475 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24476 // this.win = Roo.get(this.frameId).dom.contentWindow;
24478 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24482 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24483 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24488 initEditor : function(){
24489 //console.log("INIT EDITOR");
24490 this.assignDocWin();
24494 this.doc.designMode="on";
24496 this.doc.write(this.getDocMarkup());
24499 var dbody = (this.doc.body || this.doc.documentElement);
24500 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24501 // this copies styles from the containing element into thsi one..
24502 // not sure why we need all of this..
24503 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24505 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24506 //ss['background-attachment'] = 'fixed'; // w3c
24507 dbody.bgProperties = 'fixed'; // ie
24508 //Roo.DomHelper.applyStyles(dbody, ss);
24509 Roo.EventManager.on(this.doc, {
24510 //'mousedown': this.onEditorEvent,
24511 'mouseup': this.onEditorEvent,
24512 'dblclick': this.onEditorEvent,
24513 'click': this.onEditorEvent,
24514 'keyup': this.onEditorEvent,
24519 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24521 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24522 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24524 this.initialized = true;
24526 this.owner.fireEvent('initialize', this);
24531 onDestroy : function(){
24537 //for (var i =0; i < this.toolbars.length;i++) {
24538 // // fixme - ask toolbars for heights?
24539 // this.toolbars[i].onDestroy();
24542 //this.wrap.dom.innerHTML = '';
24543 //this.wrap.remove();
24548 onFirstFocus : function(){
24550 this.assignDocWin();
24553 this.activated = true;
24556 if(Roo.isGecko){ // prevent silly gecko errors
24558 var s = this.win.getSelection();
24559 if(!s.focusNode || s.focusNode.nodeType != 3){
24560 var r = s.getRangeAt(0);
24561 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24566 this.execCmd('useCSS', true);
24567 this.execCmd('styleWithCSS', false);
24570 this.owner.fireEvent('activate', this);
24574 adjustFont: function(btn){
24575 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24576 //if(Roo.isSafari){ // safari
24579 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24580 if(Roo.isSafari){ // safari
24581 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24582 v = (v < 10) ? 10 : v;
24583 v = (v > 48) ? 48 : v;
24584 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24589 v = Math.max(1, v+adjust);
24591 this.execCmd('FontSize', v );
24594 onEditorEvent : function(e)
24596 this.owner.fireEvent('editorevent', this, e);
24597 // this.updateToolbar();
24598 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24601 insertTag : function(tg)
24603 // could be a bit smarter... -> wrap the current selected tRoo..
24604 if (tg.toLowerCase() == 'span' ||
24605 tg.toLowerCase() == 'code' ||
24606 tg.toLowerCase() == 'sup' ||
24607 tg.toLowerCase() == 'sub'
24610 range = this.createRange(this.getSelection());
24611 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24612 wrappingNode.appendChild(range.extractContents());
24613 range.insertNode(wrappingNode);
24620 this.execCmd("formatblock", tg);
24624 insertText : function(txt)
24628 var range = this.createRange();
24629 range.deleteContents();
24630 //alert(Sender.getAttribute('label'));
24632 range.insertNode(this.doc.createTextNode(txt));
24638 * Executes a Midas editor command on the editor document and performs necessary focus and
24639 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24640 * @param {String} cmd The Midas command
24641 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24643 relayCmd : function(cmd, value){
24645 this.execCmd(cmd, value);
24646 this.owner.fireEvent('editorevent', this);
24647 //this.updateToolbar();
24648 this.owner.deferFocus();
24652 * Executes a Midas editor command directly on the editor document.
24653 * For visual commands, you should use {@link #relayCmd} instead.
24654 * <b>This should only be called after the editor is initialized.</b>
24655 * @param {String} cmd The Midas command
24656 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24658 execCmd : function(cmd, value){
24659 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24666 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24668 * @param {String} text | dom node..
24670 insertAtCursor : function(text)
24673 if(!this.activated){
24679 var r = this.doc.selection.createRange();
24690 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24694 // from jquery ui (MIT licenced)
24696 var win = this.win;
24698 if (win.getSelection && win.getSelection().getRangeAt) {
24699 range = win.getSelection().getRangeAt(0);
24700 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24701 range.insertNode(node);
24702 } else if (win.document.selection && win.document.selection.createRange) {
24703 // no firefox support
24704 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24705 win.document.selection.createRange().pasteHTML(txt);
24707 // no firefox support
24708 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24709 this.execCmd('InsertHTML', txt);
24718 mozKeyPress : function(e){
24720 var c = e.getCharCode(), cmd;
24723 c = String.fromCharCode(c).toLowerCase();
24737 this.cleanUpPaste.defer(100, this);
24745 e.preventDefault();
24753 fixKeys : function(){ // load time branching for fastest keydown performance
24755 return function(e){
24756 var k = e.getKey(), r;
24759 r = this.doc.selection.createRange();
24762 r.pasteHTML('    ');
24769 r = this.doc.selection.createRange();
24771 var target = r.parentElement();
24772 if(!target || target.tagName.toLowerCase() != 'li'){
24774 r.pasteHTML('<br />');
24780 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24781 this.cleanUpPaste.defer(100, this);
24787 }else if(Roo.isOpera){
24788 return function(e){
24789 var k = e.getKey();
24793 this.execCmd('InsertHTML','    ');
24796 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24797 this.cleanUpPaste.defer(100, this);
24802 }else if(Roo.isSafari){
24803 return function(e){
24804 var k = e.getKey();
24808 this.execCmd('InsertText','\t');
24812 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24813 this.cleanUpPaste.defer(100, this);
24821 getAllAncestors: function()
24823 var p = this.getSelectedNode();
24826 a.push(p); // push blank onto stack..
24827 p = this.getParentElement();
24831 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24835 a.push(this.doc.body);
24839 lastSelNode : false,
24842 getSelection : function()
24844 this.assignDocWin();
24845 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24848 getSelectedNode: function()
24850 // this may only work on Gecko!!!
24852 // should we cache this!!!!
24857 var range = this.createRange(this.getSelection()).cloneRange();
24860 var parent = range.parentElement();
24862 var testRange = range.duplicate();
24863 testRange.moveToElementText(parent);
24864 if (testRange.inRange(range)) {
24867 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24870 parent = parent.parentElement;
24875 // is ancestor a text element.
24876 var ac = range.commonAncestorContainer;
24877 if (ac.nodeType == 3) {
24878 ac = ac.parentNode;
24881 var ar = ac.childNodes;
24884 var other_nodes = [];
24885 var has_other_nodes = false;
24886 for (var i=0;i<ar.length;i++) {
24887 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24890 // fullly contained node.
24892 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24897 // probably selected..
24898 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24899 other_nodes.push(ar[i]);
24903 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24908 has_other_nodes = true;
24910 if (!nodes.length && other_nodes.length) {
24911 nodes= other_nodes;
24913 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24919 createRange: function(sel)
24921 // this has strange effects when using with
24922 // top toolbar - not sure if it's a great idea.
24923 //this.editor.contentWindow.focus();
24924 if (typeof sel != "undefined") {
24926 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24928 return this.doc.createRange();
24931 return this.doc.createRange();
24934 getParentElement: function()
24937 this.assignDocWin();
24938 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24940 var range = this.createRange(sel);
24943 var p = range.commonAncestorContainer;
24944 while (p.nodeType == 3) { // text node
24955 * Range intersection.. the hard stuff...
24959 * [ -- selected range --- ]
24963 * if end is before start or hits it. fail.
24964 * if start is after end or hits it fail.
24966 * if either hits (but other is outside. - then it's not
24972 // @see http://www.thismuchiknow.co.uk/?p=64.
24973 rangeIntersectsNode : function(range, node)
24975 var nodeRange = node.ownerDocument.createRange();
24977 nodeRange.selectNode(node);
24979 nodeRange.selectNodeContents(node);
24982 var rangeStartRange = range.cloneRange();
24983 rangeStartRange.collapse(true);
24985 var rangeEndRange = range.cloneRange();
24986 rangeEndRange.collapse(false);
24988 var nodeStartRange = nodeRange.cloneRange();
24989 nodeStartRange.collapse(true);
24991 var nodeEndRange = nodeRange.cloneRange();
24992 nodeEndRange.collapse(false);
24994 return rangeStartRange.compareBoundaryPoints(
24995 Range.START_TO_START, nodeEndRange) == -1 &&
24996 rangeEndRange.compareBoundaryPoints(
24997 Range.START_TO_START, nodeStartRange) == 1;
25001 rangeCompareNode : function(range, node)
25003 var nodeRange = node.ownerDocument.createRange();
25005 nodeRange.selectNode(node);
25007 nodeRange.selectNodeContents(node);
25011 range.collapse(true);
25013 nodeRange.collapse(true);
25015 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25016 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25018 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25020 var nodeIsBefore = ss == 1;
25021 var nodeIsAfter = ee == -1;
25023 if (nodeIsBefore && nodeIsAfter) {
25026 if (!nodeIsBefore && nodeIsAfter) {
25027 return 1; //right trailed.
25030 if (nodeIsBefore && !nodeIsAfter) {
25031 return 2; // left trailed.
25037 // private? - in a new class?
25038 cleanUpPaste : function()
25040 // cleans up the whole document..
25041 Roo.log('cleanuppaste');
25043 this.cleanUpChildren(this.doc.body);
25044 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25045 if (clean != this.doc.body.innerHTML) {
25046 this.doc.body.innerHTML = clean;
25051 cleanWordChars : function(input) {// change the chars to hex code
25052 var he = Roo.HtmlEditorCore;
25054 var output = input;
25055 Roo.each(he.swapCodes, function(sw) {
25056 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25058 output = output.replace(swapper, sw[1]);
25065 cleanUpChildren : function (n)
25067 if (!n.childNodes.length) {
25070 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25071 this.cleanUpChild(n.childNodes[i]);
25078 cleanUpChild : function (node)
25081 //console.log(node);
25082 if (node.nodeName == "#text") {
25083 // clean up silly Windows -- stuff?
25086 if (node.nodeName == "#comment") {
25087 node.parentNode.removeChild(node);
25088 // clean up silly Windows -- stuff?
25091 var lcname = node.tagName.toLowerCase();
25092 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25093 // whitelist of tags..
25095 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25097 node.parentNode.removeChild(node);
25102 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25104 // spans with no attributes - just remove them..
25105 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25106 remove_keep_children = true;
25109 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25110 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25112 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25113 // remove_keep_children = true;
25116 if (remove_keep_children) {
25117 this.cleanUpChildren(node);
25118 // inserts everything just before this node...
25119 while (node.childNodes.length) {
25120 var cn = node.childNodes[0];
25121 node.removeChild(cn);
25122 node.parentNode.insertBefore(cn, node);
25124 node.parentNode.removeChild(node);
25128 if (!node.attributes || !node.attributes.length) {
25133 this.cleanUpChildren(node);
25137 function cleanAttr(n,v)
25140 if (v.match(/^\./) || v.match(/^\//)) {
25143 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25146 if (v.match(/^#/)) {
25149 if (v.match(/^\{/)) { // allow template editing.
25152 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25153 node.removeAttribute(n);
25157 var cwhite = this.cwhite;
25158 var cblack = this.cblack;
25160 function cleanStyle(n,v)
25162 if (v.match(/expression/)) { //XSS?? should we even bother..
25163 node.removeAttribute(n);
25167 var parts = v.split(/;/);
25170 Roo.each(parts, function(p) {
25171 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25175 var l = p.split(':').shift().replace(/\s+/g,'');
25176 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25178 if ( cwhite.length && cblack.indexOf(l) > -1) {
25179 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25180 //node.removeAttribute(n);
25184 // only allow 'c whitelisted system attributes'
25185 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25186 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25187 //node.removeAttribute(n);
25197 if (clean.length) {
25198 node.setAttribute(n, clean.join(';'));
25200 node.removeAttribute(n);
25206 for (var i = node.attributes.length-1; i > -1 ; i--) {
25207 var a = node.attributes[i];
25210 if (a.name.toLowerCase().substr(0,2)=='on') {
25211 node.removeAttribute(a.name);
25214 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25215 node.removeAttribute(a.name);
25218 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25219 cleanAttr(a.name,a.value); // fixme..
25222 if (a.name == 'style') {
25223 cleanStyle(a.name,a.value);
25226 /// clean up MS crap..
25227 // tecnically this should be a list of valid class'es..
25230 if (a.name == 'class') {
25231 if (a.value.match(/^Mso/)) {
25232 node.removeAttribute('class');
25235 if (a.value.match(/^body$/)) {
25236 node.removeAttribute('class');
25247 this.cleanUpChildren(node);
25253 * Clean up MS wordisms...
25255 cleanWord : function(node)
25258 this.cleanWord(this.doc.body);
25263 node.nodeName == 'SPAN' &&
25264 !node.hasAttributes() &&
25265 node.childNodes.length == 1 &&
25266 node.firstChild.nodeName == "#text"
25268 var textNode = node.firstChild;
25269 node.removeChild(textNode);
25270 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25271 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25273 node.parentNode.insertBefore(textNode, node);
25274 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25275 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25277 node.parentNode.removeChild(node);
25280 if (node.nodeName == "#text") {
25281 // clean up silly Windows -- stuff?
25284 if (node.nodeName == "#comment") {
25285 node.parentNode.removeChild(node);
25286 // clean up silly Windows -- stuff?
25290 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25291 node.parentNode.removeChild(node);
25294 //Roo.log(node.tagName);
25295 // remove - but keep children..
25296 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25297 //Roo.log('-- removed');
25298 while (node.childNodes.length) {
25299 var cn = node.childNodes[0];
25300 node.removeChild(cn);
25301 node.parentNode.insertBefore(cn, node);
25302 // move node to parent - and clean it..
25303 this.cleanWord(cn);
25305 node.parentNode.removeChild(node);
25306 /// no need to iterate chidlren = it's got none..
25307 //this.iterateChildren(node, this.cleanWord);
25311 if (node.className.length) {
25313 var cn = node.className.split(/\W+/);
25315 Roo.each(cn, function(cls) {
25316 if (cls.match(/Mso[a-zA-Z]+/)) {
25321 node.className = cna.length ? cna.join(' ') : '';
25323 node.removeAttribute("class");
25327 if (node.hasAttribute("lang")) {
25328 node.removeAttribute("lang");
25331 if (node.hasAttribute("style")) {
25333 var styles = node.getAttribute("style").split(";");
25335 Roo.each(styles, function(s) {
25336 if (!s.match(/:/)) {
25339 var kv = s.split(":");
25340 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25343 // what ever is left... we allow.
25346 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25347 if (!nstyle.length) {
25348 node.removeAttribute('style');
25351 this.iterateChildren(node, this.cleanWord);
25357 * iterateChildren of a Node, calling fn each time, using this as the scole..
25358 * @param {DomNode} node node to iterate children of.
25359 * @param {Function} fn method of this class to call on each item.
25361 iterateChildren : function(node, fn)
25363 if (!node.childNodes.length) {
25366 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25367 fn.call(this, node.childNodes[i])
25373 * cleanTableWidths.
25375 * Quite often pasting from word etc.. results in tables with column and widths.
25376 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25379 cleanTableWidths : function(node)
25384 this.cleanTableWidths(this.doc.body);
25389 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25392 Roo.log(node.tagName);
25393 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25394 this.iterateChildren(node, this.cleanTableWidths);
25397 if (node.hasAttribute('width')) {
25398 node.removeAttribute('width');
25402 if (node.hasAttribute("style")) {
25405 var styles = node.getAttribute("style").split(";");
25407 Roo.each(styles, function(s) {
25408 if (!s.match(/:/)) {
25411 var kv = s.split(":");
25412 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25415 // what ever is left... we allow.
25418 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25419 if (!nstyle.length) {
25420 node.removeAttribute('style');
25424 this.iterateChildren(node, this.cleanTableWidths);
25432 domToHTML : function(currentElement, depth, nopadtext) {
25434 depth = depth || 0;
25435 nopadtext = nopadtext || false;
25437 if (!currentElement) {
25438 return this.domToHTML(this.doc.body);
25441 //Roo.log(currentElement);
25443 var allText = false;
25444 var nodeName = currentElement.nodeName;
25445 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25447 if (nodeName == '#text') {
25449 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25454 if (nodeName != 'BODY') {
25457 // Prints the node tagName, such as <A>, <IMG>, etc
25460 for(i = 0; i < currentElement.attributes.length;i++) {
25462 var aname = currentElement.attributes.item(i).name;
25463 if (!currentElement.attributes.item(i).value.length) {
25466 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25469 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25478 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25481 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25486 // Traverse the tree
25488 var currentElementChild = currentElement.childNodes.item(i);
25489 var allText = true;
25490 var innerHTML = '';
25492 while (currentElementChild) {
25493 // Formatting code (indent the tree so it looks nice on the screen)
25494 var nopad = nopadtext;
25495 if (lastnode == 'SPAN') {
25499 if (currentElementChild.nodeName == '#text') {
25500 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25501 toadd = nopadtext ? toadd : toadd.trim();
25502 if (!nopad && toadd.length > 80) {
25503 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25505 innerHTML += toadd;
25508 currentElementChild = currentElement.childNodes.item(i);
25514 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25516 // Recursively traverse the tree structure of the child node
25517 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25518 lastnode = currentElementChild.nodeName;
25520 currentElementChild=currentElement.childNodes.item(i);
25526 // The remaining code is mostly for formatting the tree
25527 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25532 ret+= "</"+tagName+">";
25538 applyBlacklists : function()
25540 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25541 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25545 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25546 if (b.indexOf(tag) > -1) {
25549 this.white.push(tag);
25553 Roo.each(w, function(tag) {
25554 if (b.indexOf(tag) > -1) {
25557 if (this.white.indexOf(tag) > -1) {
25560 this.white.push(tag);
25565 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25566 if (w.indexOf(tag) > -1) {
25569 this.black.push(tag);
25573 Roo.each(b, function(tag) {
25574 if (w.indexOf(tag) > -1) {
25577 if (this.black.indexOf(tag) > -1) {
25580 this.black.push(tag);
25585 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25586 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25590 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25591 if (b.indexOf(tag) > -1) {
25594 this.cwhite.push(tag);
25598 Roo.each(w, function(tag) {
25599 if (b.indexOf(tag) > -1) {
25602 if (this.cwhite.indexOf(tag) > -1) {
25605 this.cwhite.push(tag);
25610 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25611 if (w.indexOf(tag) > -1) {
25614 this.cblack.push(tag);
25618 Roo.each(b, function(tag) {
25619 if (w.indexOf(tag) > -1) {
25622 if (this.cblack.indexOf(tag) > -1) {
25625 this.cblack.push(tag);
25630 setStylesheets : function(stylesheets)
25632 if(typeof(stylesheets) == 'string'){
25633 Roo.get(this.iframe.contentDocument.head).createChild({
25635 rel : 'stylesheet',
25644 Roo.each(stylesheets, function(s) {
25649 Roo.get(_this.iframe.contentDocument.head).createChild({
25651 rel : 'stylesheet',
25660 removeStylesheets : function()
25664 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25669 setStyle : function(style)
25671 Roo.get(this.iframe.contentDocument.head).createChild({
25680 // hide stuff that is not compatible
25694 * @event specialkey
25698 * @cfg {String} fieldClass @hide
25701 * @cfg {String} focusClass @hide
25704 * @cfg {String} autoCreate @hide
25707 * @cfg {String} inputType @hide
25710 * @cfg {String} invalidClass @hide
25713 * @cfg {String} invalidText @hide
25716 * @cfg {String} msgFx @hide
25719 * @cfg {String} validateOnBlur @hide
25723 Roo.HtmlEditorCore.white = [
25724 'area', 'br', 'img', 'input', 'hr', 'wbr',
25726 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25727 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25728 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25729 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25730 'table', 'ul', 'xmp',
25732 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25735 'dir', 'menu', 'ol', 'ul', 'dl',
25741 Roo.HtmlEditorCore.black = [
25742 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25744 'base', 'basefont', 'bgsound', 'blink', 'body',
25745 'frame', 'frameset', 'head', 'html', 'ilayer',
25746 'iframe', 'layer', 'link', 'meta', 'object',
25747 'script', 'style' ,'title', 'xml' // clean later..
25749 Roo.HtmlEditorCore.clean = [
25750 'script', 'style', 'title', 'xml'
25752 Roo.HtmlEditorCore.remove = [
25757 Roo.HtmlEditorCore.ablack = [
25761 Roo.HtmlEditorCore.aclean = [
25762 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25766 Roo.HtmlEditorCore.pwhite= [
25767 'http', 'https', 'mailto'
25770 // white listed style attributes.
25771 Roo.HtmlEditorCore.cwhite= [
25772 // 'text-align', /// default is to allow most things..
25778 // black listed style attributes.
25779 Roo.HtmlEditorCore.cblack= [
25780 // 'font-size' -- this can be set by the project
25784 Roo.HtmlEditorCore.swapCodes =[
25803 * @class Roo.bootstrap.HtmlEditor
25804 * @extends Roo.bootstrap.TextArea
25805 * Bootstrap HtmlEditor class
25808 * Create a new HtmlEditor
25809 * @param {Object} config The config object
25812 Roo.bootstrap.HtmlEditor = function(config){
25813 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25814 if (!this.toolbars) {
25815 this.toolbars = [];
25818 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25821 * @event initialize
25822 * Fires when the editor is fully initialized (including the iframe)
25823 * @param {HtmlEditor} this
25828 * Fires when the editor is first receives the focus. Any insertion must wait
25829 * until after this event.
25830 * @param {HtmlEditor} this
25834 * @event beforesync
25835 * Fires before the textarea is updated with content from the editor iframe. Return false
25836 * to cancel the sync.
25837 * @param {HtmlEditor} this
25838 * @param {String} html
25842 * @event beforepush
25843 * Fires before the iframe editor is updated with content from the textarea. Return false
25844 * to cancel the push.
25845 * @param {HtmlEditor} this
25846 * @param {String} html
25851 * Fires when the textarea is updated with content from the editor iframe.
25852 * @param {HtmlEditor} this
25853 * @param {String} html
25858 * Fires when the iframe editor is updated with content from the textarea.
25859 * @param {HtmlEditor} this
25860 * @param {String} html
25864 * @event editmodechange
25865 * Fires when the editor switches edit modes
25866 * @param {HtmlEditor} this
25867 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25869 editmodechange: true,
25871 * @event editorevent
25872 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25873 * @param {HtmlEditor} this
25877 * @event firstfocus
25878 * Fires when on first focus - needed by toolbars..
25879 * @param {HtmlEditor} this
25884 * Auto save the htmlEditor value as a file into Events
25885 * @param {HtmlEditor} this
25889 * @event savedpreview
25890 * preview the saved version of htmlEditor
25891 * @param {HtmlEditor} this
25898 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25902 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25907 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25912 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25917 * @cfg {Number} height (in pixels)
25921 * @cfg {Number} width (in pixels)
25926 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25929 stylesheets: false,
25934 // private properties
25935 validationEvent : false,
25937 initialized : false,
25940 onFocus : Roo.emptyFn,
25942 hideMode:'offsets',
25944 tbContainer : false,
25948 toolbarContainer :function() {
25949 return this.wrap.select('.x-html-editor-tb',true).first();
25953 * Protected method that will not generally be called directly. It
25954 * is called when the editor creates its toolbar. Override this method if you need to
25955 * add custom toolbar buttons.
25956 * @param {HtmlEditor} editor
25958 createToolbar : function(){
25959 Roo.log('renewing');
25960 Roo.log("create toolbars");
25962 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25963 this.toolbars[0].render(this.toolbarContainer());
25967 // if (!editor.toolbars || !editor.toolbars.length) {
25968 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25971 // for (var i =0 ; i < editor.toolbars.length;i++) {
25972 // editor.toolbars[i] = Roo.factory(
25973 // typeof(editor.toolbars[i]) == 'string' ?
25974 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25975 // Roo.bootstrap.HtmlEditor);
25976 // editor.toolbars[i].init(editor);
25982 onRender : function(ct, position)
25984 // Roo.log("Call onRender: " + this.xtype);
25986 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25988 this.wrap = this.inputEl().wrap({
25989 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25992 this.editorcore.onRender(ct, position);
25994 if (this.resizable) {
25995 this.resizeEl = new Roo.Resizable(this.wrap, {
25999 minHeight : this.height,
26000 height: this.height,
26001 handles : this.resizable,
26004 resize : function(r, w, h) {
26005 _t.onResize(w,h); // -something
26011 this.createToolbar(this);
26014 if(!this.width && this.resizable){
26015 this.setSize(this.wrap.getSize());
26017 if (this.resizeEl) {
26018 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26019 // should trigger onReize..
26025 onResize : function(w, h)
26027 Roo.log('resize: ' +w + ',' + h );
26028 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26032 if(this.inputEl() ){
26033 if(typeof w == 'number'){
26034 var aw = w - this.wrap.getFrameWidth('lr');
26035 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26038 if(typeof h == 'number'){
26039 var tbh = -11; // fixme it needs to tool bar size!
26040 for (var i =0; i < this.toolbars.length;i++) {
26041 // fixme - ask toolbars for heights?
26042 tbh += this.toolbars[i].el.getHeight();
26043 //if (this.toolbars[i].footer) {
26044 // tbh += this.toolbars[i].footer.el.getHeight();
26052 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26053 ah -= 5; // knock a few pixes off for look..
26054 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26058 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26059 this.editorcore.onResize(ew,eh);
26064 * Toggles the editor between standard and source edit mode.
26065 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26067 toggleSourceEdit : function(sourceEditMode)
26069 this.editorcore.toggleSourceEdit(sourceEditMode);
26071 if(this.editorcore.sourceEditMode){
26072 Roo.log('editor - showing textarea');
26075 // Roo.log(this.syncValue());
26077 this.inputEl().removeClass(['hide', 'x-hidden']);
26078 this.inputEl().dom.removeAttribute('tabIndex');
26079 this.inputEl().focus();
26081 Roo.log('editor - hiding textarea');
26083 // Roo.log(this.pushValue());
26086 this.inputEl().addClass(['hide', 'x-hidden']);
26087 this.inputEl().dom.setAttribute('tabIndex', -1);
26088 //this.deferFocus();
26091 if(this.resizable){
26092 this.setSize(this.wrap.getSize());
26095 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26098 // private (for BoxComponent)
26099 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26101 // private (for BoxComponent)
26102 getResizeEl : function(){
26106 // private (for BoxComponent)
26107 getPositionEl : function(){
26112 initEvents : function(){
26113 this.originalValue = this.getValue();
26117 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26120 // markInvalid : Roo.emptyFn,
26122 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26125 // clearInvalid : Roo.emptyFn,
26127 setValue : function(v){
26128 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26129 this.editorcore.pushValue();
26134 deferFocus : function(){
26135 this.focus.defer(10, this);
26139 focus : function(){
26140 this.editorcore.focus();
26146 onDestroy : function(){
26152 for (var i =0; i < this.toolbars.length;i++) {
26153 // fixme - ask toolbars for heights?
26154 this.toolbars[i].onDestroy();
26157 this.wrap.dom.innerHTML = '';
26158 this.wrap.remove();
26163 onFirstFocus : function(){
26164 //Roo.log("onFirstFocus");
26165 this.editorcore.onFirstFocus();
26166 for (var i =0; i < this.toolbars.length;i++) {
26167 this.toolbars[i].onFirstFocus();
26173 syncValue : function()
26175 this.editorcore.syncValue();
26178 pushValue : function()
26180 this.editorcore.pushValue();
26184 // hide stuff that is not compatible
26198 * @event specialkey
26202 * @cfg {String} fieldClass @hide
26205 * @cfg {String} focusClass @hide
26208 * @cfg {String} autoCreate @hide
26211 * @cfg {String} inputType @hide
26215 * @cfg {String} invalidText @hide
26218 * @cfg {String} msgFx @hide
26221 * @cfg {String} validateOnBlur @hide
26230 Roo.namespace('Roo.bootstrap.htmleditor');
26232 * @class Roo.bootstrap.HtmlEditorToolbar1
26238 new Roo.bootstrap.HtmlEditor({
26241 new Roo.bootstrap.HtmlEditorToolbar1({
26242 disable : { fonts: 1 , format: 1, ..., ... , ...],
26248 * @cfg {Object} disable List of elements to disable..
26249 * @cfg {Array} btns List of additional buttons.
26253 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26256 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26259 Roo.apply(this, config);
26261 // default disabled, based on 'good practice'..
26262 this.disable = this.disable || {};
26263 Roo.applyIf(this.disable, {
26266 specialElements : true
26268 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26270 this.editor = config.editor;
26271 this.editorcore = config.editor.editorcore;
26273 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26275 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26276 // dont call parent... till later.
26278 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26283 editorcore : false,
26288 "h1","h2","h3","h4","h5","h6",
26290 "abbr", "acronym", "address", "cite", "samp", "var",
26294 onRender : function(ct, position)
26296 // Roo.log("Call onRender: " + this.xtype);
26298 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26300 this.el.dom.style.marginBottom = '0';
26302 var editorcore = this.editorcore;
26303 var editor= this.editor;
26306 var btn = function(id,cmd , toggle, handler, html){
26308 var event = toggle ? 'toggle' : 'click';
26313 xns: Roo.bootstrap,
26317 enableToggle:toggle !== false,
26319 pressed : toggle ? false : null,
26322 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26323 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26329 // var cb_box = function...
26334 xns: Roo.bootstrap,
26339 xns: Roo.bootstrap,
26343 Roo.each(this.formats, function(f) {
26344 style.menu.items.push({
26346 xns: Roo.bootstrap,
26347 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26352 editorcore.insertTag(this.tagname);
26359 children.push(style);
26361 btn('bold',false,true);
26362 btn('italic',false,true);
26363 btn('align-left', 'justifyleft',true);
26364 btn('align-center', 'justifycenter',true);
26365 btn('align-right' , 'justifyright',true);
26366 btn('link', false, false, function(btn) {
26367 //Roo.log("create link?");
26368 var url = prompt(this.createLinkText, this.defaultLinkValue);
26369 if(url && url != 'http:/'+'/'){
26370 this.editorcore.relayCmd('createlink', url);
26373 btn('list','insertunorderedlist',true);
26374 btn('pencil', false,true, function(btn){
26376 this.toggleSourceEdit(btn.pressed);
26379 if (this.editor.btns.length > 0) {
26380 for (var i = 0; i<this.editor.btns.length; i++) {
26381 children.push(this.editor.btns[i]);
26389 xns: Roo.bootstrap,
26394 xns: Roo.bootstrap,
26399 cog.menu.items.push({
26401 xns: Roo.bootstrap,
26402 html : Clean styles,
26407 editorcore.insertTag(this.tagname);
26416 this.xtype = 'NavSimplebar';
26418 for(var i=0;i< children.length;i++) {
26420 this.buttons.add(this.addxtypeChild(children[i]));
26424 editor.on('editorevent', this.updateToolbar, this);
26426 onBtnClick : function(id)
26428 this.editorcore.relayCmd(id);
26429 this.editorcore.focus();
26433 * Protected method that will not generally be called directly. It triggers
26434 * a toolbar update by reading the markup state of the current selection in the editor.
26436 updateToolbar: function(){
26438 if(!this.editorcore.activated){
26439 this.editor.onFirstFocus(); // is this neeed?
26443 var btns = this.buttons;
26444 var doc = this.editorcore.doc;
26445 btns.get('bold').setActive(doc.queryCommandState('bold'));
26446 btns.get('italic').setActive(doc.queryCommandState('italic'));
26447 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26449 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26450 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26451 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26453 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26454 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26457 var ans = this.editorcore.getAllAncestors();
26458 if (this.formatCombo) {
26461 var store = this.formatCombo.store;
26462 this.formatCombo.setValue("");
26463 for (var i =0; i < ans.length;i++) {
26464 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26466 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26474 // hides menus... - so this cant be on a menu...
26475 Roo.bootstrap.MenuMgr.hideAll();
26477 Roo.bootstrap.MenuMgr.hideAll();
26478 //this.editorsyncValue();
26480 onFirstFocus: function() {
26481 this.buttons.each(function(item){
26485 toggleSourceEdit : function(sourceEditMode){
26488 if(sourceEditMode){
26489 Roo.log("disabling buttons");
26490 this.buttons.each( function(item){
26491 if(item.cmd != 'pencil'){
26497 Roo.log("enabling buttons");
26498 if(this.editorcore.initialized){
26499 this.buttons.each( function(item){
26505 Roo.log("calling toggole on editor");
26506 // tell the editor that it's been pressed..
26507 this.editor.toggleSourceEdit(sourceEditMode);
26521 * @class Roo.bootstrap.Markdown
26522 * @extends Roo.bootstrap.TextArea
26523 * Bootstrap Showdown editable area
26524 * @cfg {string} content
26527 * Create a new Showdown
26530 Roo.bootstrap.Markdown = function(config){
26531 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26535 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26539 initEvents : function()
26542 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26543 this.markdownEl = this.el.createChild({
26544 cls : 'roo-markdown-area'
26546 this.inputEl().addClass('d-none');
26547 if (this.getValue() == '') {
26548 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26551 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26553 this.markdownEl.on('click', this.toggleTextEdit, this);
26554 this.on('blur', this.toggleTextEdit, this);
26555 this.on('specialkey', this.resizeTextArea, this);
26558 toggleTextEdit : function()
26560 var sh = this.markdownEl.getHeight();
26561 this.inputEl().addClass('d-none');
26562 this.markdownEl.addClass('d-none');
26563 if (!this.editing) {
26565 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26566 this.inputEl().removeClass('d-none');
26567 this.inputEl().focus();
26568 this.editing = true;
26571 // show showdown...
26572 this.updateMarkdown();
26573 this.markdownEl.removeClass('d-none');
26574 this.editing = false;
26577 updateMarkdown : function()
26579 if (this.getValue() == '') {
26580 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26584 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26587 resizeTextArea: function () {
26590 Roo.log([sh, this.getValue().split("\n").length * 30]);
26591 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26593 setValue : function(val)
26595 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26596 if (!this.editing) {
26597 this.updateMarkdown();
26603 if (!this.editing) {
26604 this.toggleTextEdit();
26612 * @class Roo.bootstrap.Table.AbstractSelectionModel
26613 * @extends Roo.util.Observable
26614 * Abstract base class for grid SelectionModels. It provides the interface that should be
26615 * implemented by descendant classes. This class should not be directly instantiated.
26618 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26619 this.locked = false;
26620 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26624 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26625 /** @ignore Called by the grid automatically. Do not call directly. */
26626 init : function(grid){
26632 * Locks the selections.
26635 this.locked = true;
26639 * Unlocks the selections.
26641 unlock : function(){
26642 this.locked = false;
26646 * Returns true if the selections are locked.
26647 * @return {Boolean}
26649 isLocked : function(){
26650 return this.locked;
26654 initEvents : function ()
26660 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26661 * @class Roo.bootstrap.Table.RowSelectionModel
26662 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26663 * It supports multiple selections and keyboard selection/navigation.
26665 * @param {Object} config
26668 Roo.bootstrap.Table.RowSelectionModel = function(config){
26669 Roo.apply(this, config);
26670 this.selections = new Roo.util.MixedCollection(false, function(o){
26675 this.lastActive = false;
26679 * @event selectionchange
26680 * Fires when the selection changes
26681 * @param {SelectionModel} this
26683 "selectionchange" : true,
26685 * @event afterselectionchange
26686 * Fires after the selection changes (eg. by key press or clicking)
26687 * @param {SelectionModel} this
26689 "afterselectionchange" : true,
26691 * @event beforerowselect
26692 * Fires when a row is selected being selected, return false to cancel.
26693 * @param {SelectionModel} this
26694 * @param {Number} rowIndex The selected index
26695 * @param {Boolean} keepExisting False if other selections will be cleared
26697 "beforerowselect" : true,
26700 * Fires when a row is selected.
26701 * @param {SelectionModel} this
26702 * @param {Number} rowIndex The selected index
26703 * @param {Roo.data.Record} r The record
26705 "rowselect" : true,
26707 * @event rowdeselect
26708 * Fires when a row is deselected.
26709 * @param {SelectionModel} this
26710 * @param {Number} rowIndex The selected index
26712 "rowdeselect" : true
26714 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26715 this.locked = false;
26718 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26720 * @cfg {Boolean} singleSelect
26721 * True to allow selection of only one row at a time (defaults to false)
26723 singleSelect : false,
26726 initEvents : function()
26729 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26730 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26731 //}else{ // allow click to work like normal
26732 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26734 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26735 this.grid.on("rowclick", this.handleMouseDown, this);
26737 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26738 "up" : function(e){
26740 this.selectPrevious(e.shiftKey);
26741 }else if(this.last !== false && this.lastActive !== false){
26742 var last = this.last;
26743 this.selectRange(this.last, this.lastActive-1);
26744 this.grid.getView().focusRow(this.lastActive);
26745 if(last !== false){
26749 this.selectFirstRow();
26751 this.fireEvent("afterselectionchange", this);
26753 "down" : function(e){
26755 this.selectNext(e.shiftKey);
26756 }else if(this.last !== false && this.lastActive !== false){
26757 var last = this.last;
26758 this.selectRange(this.last, this.lastActive+1);
26759 this.grid.getView().focusRow(this.lastActive);
26760 if(last !== false){
26764 this.selectFirstRow();
26766 this.fireEvent("afterselectionchange", this);
26770 this.grid.store.on('load', function(){
26771 this.selections.clear();
26774 var view = this.grid.view;
26775 view.on("refresh", this.onRefresh, this);
26776 view.on("rowupdated", this.onRowUpdated, this);
26777 view.on("rowremoved", this.onRemove, this);
26782 onRefresh : function()
26784 var ds = this.grid.store, i, v = this.grid.view;
26785 var s = this.selections;
26786 s.each(function(r){
26787 if((i = ds.indexOfId(r.id)) != -1){
26796 onRemove : function(v, index, r){
26797 this.selections.remove(r);
26801 onRowUpdated : function(v, index, r){
26802 if(this.isSelected(r)){
26803 v.onRowSelect(index);
26809 * @param {Array} records The records to select
26810 * @param {Boolean} keepExisting (optional) True to keep existing selections
26812 selectRecords : function(records, keepExisting)
26815 this.clearSelections();
26817 var ds = this.grid.store;
26818 for(var i = 0, len = records.length; i < len; i++){
26819 this.selectRow(ds.indexOf(records[i]), true);
26824 * Gets the number of selected rows.
26827 getCount : function(){
26828 return this.selections.length;
26832 * Selects the first row in the grid.
26834 selectFirstRow : function(){
26839 * Select the last row.
26840 * @param {Boolean} keepExisting (optional) True to keep existing selections
26842 selectLastRow : function(keepExisting){
26843 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26844 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26848 * Selects the row immediately following the last selected row.
26849 * @param {Boolean} keepExisting (optional) True to keep existing selections
26851 selectNext : function(keepExisting)
26853 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26854 this.selectRow(this.last+1, keepExisting);
26855 this.grid.getView().focusRow(this.last);
26860 * Selects the row that precedes the last selected row.
26861 * @param {Boolean} keepExisting (optional) True to keep existing selections
26863 selectPrevious : function(keepExisting){
26865 this.selectRow(this.last-1, keepExisting);
26866 this.grid.getView().focusRow(this.last);
26871 * Returns the selected records
26872 * @return {Array} Array of selected records
26874 getSelections : function(){
26875 return [].concat(this.selections.items);
26879 * Returns the first selected record.
26882 getSelected : function(){
26883 return this.selections.itemAt(0);
26888 * Clears all selections.
26890 clearSelections : function(fast)
26896 var ds = this.grid.store;
26897 var s = this.selections;
26898 s.each(function(r){
26899 this.deselectRow(ds.indexOfId(r.id));
26903 this.selections.clear();
26910 * Selects all rows.
26912 selectAll : function(){
26916 this.selections.clear();
26917 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26918 this.selectRow(i, true);
26923 * Returns True if there is a selection.
26924 * @return {Boolean}
26926 hasSelection : function(){
26927 return this.selections.length > 0;
26931 * Returns True if the specified row is selected.
26932 * @param {Number/Record} record The record or index of the record to check
26933 * @return {Boolean}
26935 isSelected : function(index){
26936 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26937 return (r && this.selections.key(r.id) ? true : false);
26941 * Returns True if the specified record id is selected.
26942 * @param {String} id The id of record to check
26943 * @return {Boolean}
26945 isIdSelected : function(id){
26946 return (this.selections.key(id) ? true : false);
26951 handleMouseDBClick : function(e, t){
26955 handleMouseDown : function(e, t)
26957 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26958 if(this.isLocked() || rowIndex < 0 ){
26961 if(e.shiftKey && this.last !== false){
26962 var last = this.last;
26963 this.selectRange(last, rowIndex, e.ctrlKey);
26964 this.last = last; // reset the last
26968 var isSelected = this.isSelected(rowIndex);
26969 //Roo.log("select row:" + rowIndex);
26971 this.deselectRow(rowIndex);
26973 this.selectRow(rowIndex, true);
26977 if(e.button !== 0 && isSelected){
26978 alert('rowIndex 2: ' + rowIndex);
26979 view.focusRow(rowIndex);
26980 }else if(e.ctrlKey && isSelected){
26981 this.deselectRow(rowIndex);
26982 }else if(!isSelected){
26983 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26984 view.focusRow(rowIndex);
26988 this.fireEvent("afterselectionchange", this);
26991 handleDragableRowClick : function(grid, rowIndex, e)
26993 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26994 this.selectRow(rowIndex, false);
26995 grid.view.focusRow(rowIndex);
26996 this.fireEvent("afterselectionchange", this);
27001 * Selects multiple rows.
27002 * @param {Array} rows Array of the indexes of the row to select
27003 * @param {Boolean} keepExisting (optional) True to keep existing selections
27005 selectRows : function(rows, keepExisting){
27007 this.clearSelections();
27009 for(var i = 0, len = rows.length; i < len; i++){
27010 this.selectRow(rows[i], true);
27015 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27016 * @param {Number} startRow The index of the first row in the range
27017 * @param {Number} endRow The index of the last row in the range
27018 * @param {Boolean} keepExisting (optional) True to retain existing selections
27020 selectRange : function(startRow, endRow, keepExisting){
27025 this.clearSelections();
27027 if(startRow <= endRow){
27028 for(var i = startRow; i <= endRow; i++){
27029 this.selectRow(i, true);
27032 for(var i = startRow; i >= endRow; i--){
27033 this.selectRow(i, true);
27039 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27040 * @param {Number} startRow The index of the first row in the range
27041 * @param {Number} endRow The index of the last row in the range
27043 deselectRange : function(startRow, endRow, preventViewNotify){
27047 for(var i = startRow; i <= endRow; i++){
27048 this.deselectRow(i, preventViewNotify);
27054 * @param {Number} row The index of the row to select
27055 * @param {Boolean} keepExisting (optional) True to keep existing selections
27057 selectRow : function(index, keepExisting, preventViewNotify)
27059 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27062 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27063 if(!keepExisting || this.singleSelect){
27064 this.clearSelections();
27067 var r = this.grid.store.getAt(index);
27068 //console.log('selectRow - record id :' + r.id);
27070 this.selections.add(r);
27071 this.last = this.lastActive = index;
27072 if(!preventViewNotify){
27073 var proxy = new Roo.Element(
27074 this.grid.getRowDom(index)
27076 proxy.addClass('bg-info info');
27078 this.fireEvent("rowselect", this, index, r);
27079 this.fireEvent("selectionchange", this);
27085 * @param {Number} row The index of the row to deselect
27087 deselectRow : function(index, preventViewNotify)
27092 if(this.last == index){
27095 if(this.lastActive == index){
27096 this.lastActive = false;
27099 var r = this.grid.store.getAt(index);
27104 this.selections.remove(r);
27105 //.console.log('deselectRow - record id :' + r.id);
27106 if(!preventViewNotify){
27108 var proxy = new Roo.Element(
27109 this.grid.getRowDom(index)
27111 proxy.removeClass('bg-info info');
27113 this.fireEvent("rowdeselect", this, index);
27114 this.fireEvent("selectionchange", this);
27118 restoreLast : function(){
27120 this.last = this._last;
27125 acceptsNav : function(row, col, cm){
27126 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27130 onEditorKey : function(field, e){
27131 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27136 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27138 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27140 }else if(k == e.ENTER && !e.ctrlKey){
27144 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27146 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27148 }else if(k == e.ESC){
27152 g.startEditing(newCell[0], newCell[1]);
27158 * Ext JS Library 1.1.1
27159 * Copyright(c) 2006-2007, Ext JS, LLC.
27161 * Originally Released Under LGPL - original licence link has changed is not relivant.
27164 * <script type="text/javascript">
27168 * @class Roo.bootstrap.PagingToolbar
27169 * @extends Roo.bootstrap.NavSimplebar
27170 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27172 * Create a new PagingToolbar
27173 * @param {Object} config The config object
27174 * @param {Roo.data.Store} store
27176 Roo.bootstrap.PagingToolbar = function(config)
27178 // old args format still supported... - xtype is prefered..
27179 // created from xtype...
27181 this.ds = config.dataSource;
27183 if (config.store && !this.ds) {
27184 this.store= Roo.factory(config.store, Roo.data);
27185 this.ds = this.store;
27186 this.ds.xmodule = this.xmodule || false;
27189 this.toolbarItems = [];
27190 if (config.items) {
27191 this.toolbarItems = config.items;
27194 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27199 this.bind(this.ds);
27202 if (Roo.bootstrap.version == 4) {
27203 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27205 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27210 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27212 * @cfg {Roo.data.Store} dataSource
27213 * The underlying data store providing the paged data
27216 * @cfg {String/HTMLElement/Element} container
27217 * container The id or element that will contain the toolbar
27220 * @cfg {Boolean} displayInfo
27221 * True to display the displayMsg (defaults to false)
27224 * @cfg {Number} pageSize
27225 * The number of records to display per page (defaults to 20)
27229 * @cfg {String} displayMsg
27230 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27232 displayMsg : 'Displaying {0} - {1} of {2}',
27234 * @cfg {String} emptyMsg
27235 * The message to display when no records are found (defaults to "No data to display")
27237 emptyMsg : 'No data to display',
27239 * Customizable piece of the default paging text (defaults to "Page")
27242 beforePageText : "Page",
27244 * Customizable piece of the default paging text (defaults to "of %0")
27247 afterPageText : "of {0}",
27249 * Customizable piece of the default paging text (defaults to "First Page")
27252 firstText : "First Page",
27254 * Customizable piece of the default paging text (defaults to "Previous Page")
27257 prevText : "Previous Page",
27259 * Customizable piece of the default paging text (defaults to "Next Page")
27262 nextText : "Next Page",
27264 * Customizable piece of the default paging text (defaults to "Last Page")
27267 lastText : "Last Page",
27269 * Customizable piece of the default paging text (defaults to "Refresh")
27272 refreshText : "Refresh",
27276 onRender : function(ct, position)
27278 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27279 this.navgroup.parentId = this.id;
27280 this.navgroup.onRender(this.el, null);
27281 // add the buttons to the navgroup
27283 if(this.displayInfo){
27284 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27285 this.displayEl = this.el.select('.x-paging-info', true).first();
27286 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27287 // this.displayEl = navel.el.select('span',true).first();
27293 Roo.each(_this.buttons, function(e){ // this might need to use render????
27294 Roo.factory(e).render(_this.el);
27298 Roo.each(_this.toolbarItems, function(e) {
27299 _this.navgroup.addItem(e);
27303 this.first = this.navgroup.addItem({
27304 tooltip: this.firstText,
27305 cls: "prev btn-outline-secondary",
27306 html : ' <i class="fa fa-step-backward"></i>',
27308 preventDefault: true,
27309 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27312 this.prev = this.navgroup.addItem({
27313 tooltip: this.prevText,
27314 cls: "prev btn-outline-secondary",
27315 html : ' <i class="fa fa-backward"></i>',
27317 preventDefault: true,
27318 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27320 //this.addSeparator();
27323 var field = this.navgroup.addItem( {
27325 cls : 'x-paging-position btn-outline-secondary',
27327 html : this.beforePageText +
27328 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27329 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27332 this.field = field.el.select('input', true).first();
27333 this.field.on("keydown", this.onPagingKeydown, this);
27334 this.field.on("focus", function(){this.dom.select();});
27337 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27338 //this.field.setHeight(18);
27339 //this.addSeparator();
27340 this.next = this.navgroup.addItem({
27341 tooltip: this.nextText,
27342 cls: "next btn-outline-secondary",
27343 html : ' <i class="fa fa-forward"></i>',
27345 preventDefault: true,
27346 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27348 this.last = this.navgroup.addItem({
27349 tooltip: this.lastText,
27350 html : ' <i class="fa fa-step-forward"></i>',
27351 cls: "next btn-outline-secondary",
27353 preventDefault: true,
27354 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27356 //this.addSeparator();
27357 this.loading = this.navgroup.addItem({
27358 tooltip: this.refreshText,
27359 cls: "btn-outline-secondary",
27360 html : ' <i class="fa fa-refresh"></i>',
27361 preventDefault: true,
27362 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27368 updateInfo : function(){
27369 if(this.displayEl){
27370 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27371 var msg = count == 0 ?
27375 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27377 this.displayEl.update(msg);
27382 onLoad : function(ds, r, o)
27384 this.cursor = o.params && o.params.start ? o.params.start : 0;
27386 var d = this.getPageData(),
27391 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27392 this.field.dom.value = ap;
27393 this.first.setDisabled(ap == 1);
27394 this.prev.setDisabled(ap == 1);
27395 this.next.setDisabled(ap == ps);
27396 this.last.setDisabled(ap == ps);
27397 this.loading.enable();
27402 getPageData : function(){
27403 var total = this.ds.getTotalCount();
27406 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27407 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27412 onLoadError : function(){
27413 this.loading.enable();
27417 onPagingKeydown : function(e){
27418 var k = e.getKey();
27419 var d = this.getPageData();
27421 var v = this.field.dom.value, pageNum;
27422 if(!v || isNaN(pageNum = parseInt(v, 10))){
27423 this.field.dom.value = d.activePage;
27426 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27427 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27430 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))
27432 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27433 this.field.dom.value = pageNum;
27434 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27437 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27439 var v = this.field.dom.value, pageNum;
27440 var increment = (e.shiftKey) ? 10 : 1;
27441 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27444 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27445 this.field.dom.value = d.activePage;
27448 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27450 this.field.dom.value = parseInt(v, 10) + increment;
27451 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27452 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27459 beforeLoad : function(){
27461 this.loading.disable();
27466 onClick : function(which){
27475 ds.load({params:{start: 0, limit: this.pageSize}});
27478 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27481 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27484 var total = ds.getTotalCount();
27485 var extra = total % this.pageSize;
27486 var lastStart = extra ? (total - extra) : total-this.pageSize;
27487 ds.load({params:{start: lastStart, limit: this.pageSize}});
27490 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27496 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27497 * @param {Roo.data.Store} store The data store to unbind
27499 unbind : function(ds){
27500 ds.un("beforeload", this.beforeLoad, this);
27501 ds.un("load", this.onLoad, this);
27502 ds.un("loadexception", this.onLoadError, this);
27503 ds.un("remove", this.updateInfo, this);
27504 ds.un("add", this.updateInfo, this);
27505 this.ds = undefined;
27509 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27510 * @param {Roo.data.Store} store The data store to bind
27512 bind : function(ds){
27513 ds.on("beforeload", this.beforeLoad, this);
27514 ds.on("load", this.onLoad, this);
27515 ds.on("loadexception", this.onLoadError, this);
27516 ds.on("remove", this.updateInfo, this);
27517 ds.on("add", this.updateInfo, this);
27528 * @class Roo.bootstrap.MessageBar
27529 * @extends Roo.bootstrap.Component
27530 * Bootstrap MessageBar class
27531 * @cfg {String} html contents of the MessageBar
27532 * @cfg {String} weight (info | success | warning | danger) default info
27533 * @cfg {String} beforeClass insert the bar before the given class
27534 * @cfg {Boolean} closable (true | false) default false
27535 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27538 * Create a new Element
27539 * @param {Object} config The config object
27542 Roo.bootstrap.MessageBar = function(config){
27543 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27546 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27552 beforeClass: 'bootstrap-sticky-wrap',
27554 getAutoCreate : function(){
27558 cls: 'alert alert-dismissable alert-' + this.weight,
27563 html: this.html || ''
27569 cfg.cls += ' alert-messages-fixed';
27583 onRender : function(ct, position)
27585 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27588 var cfg = Roo.apply({}, this.getAutoCreate());
27592 cfg.cls += ' ' + this.cls;
27595 cfg.style = this.style;
27597 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27599 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27602 this.el.select('>button.close').on('click', this.hide, this);
27608 if (!this.rendered) {
27614 this.fireEvent('show', this);
27620 if (!this.rendered) {
27626 this.fireEvent('hide', this);
27629 update : function()
27631 // var e = this.el.dom.firstChild;
27633 // if(this.closable){
27634 // e = e.nextSibling;
27637 // e.data = this.html || '';
27639 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27655 * @class Roo.bootstrap.Graph
27656 * @extends Roo.bootstrap.Component
27657 * Bootstrap Graph class
27661 @cfg {String} graphtype bar | vbar | pie
27662 @cfg {number} g_x coodinator | centre x (pie)
27663 @cfg {number} g_y coodinator | centre y (pie)
27664 @cfg {number} g_r radius (pie)
27665 @cfg {number} g_height height of the chart (respected by all elements in the set)
27666 @cfg {number} g_width width of the chart (respected by all elements in the set)
27667 @cfg {Object} title The title of the chart
27670 -opts (object) options for the chart
27672 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27673 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27675 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.
27676 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27678 o stretch (boolean)
27680 -opts (object) options for the pie
27683 o startAngle (number)
27684 o endAngle (number)
27688 * Create a new Input
27689 * @param {Object} config The config object
27692 Roo.bootstrap.Graph = function(config){
27693 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27699 * The img click event for the img.
27700 * @param {Roo.EventObject} e
27706 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27717 //g_colors: this.colors,
27724 getAutoCreate : function(){
27735 onRender : function(ct,position){
27738 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27740 if (typeof(Raphael) == 'undefined') {
27741 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27745 this.raphael = Raphael(this.el.dom);
27747 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27748 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27749 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27750 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27752 r.text(160, 10, "Single Series Chart").attr(txtattr);
27753 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27754 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27755 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27757 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27758 r.barchart(330, 10, 300, 220, data1);
27759 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27760 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27763 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27764 // r.barchart(30, 30, 560, 250, xdata, {
27765 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27766 // axis : "0 0 1 1",
27767 // axisxlabels : xdata
27768 // //yvalues : cols,
27771 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27773 // this.load(null,xdata,{
27774 // axis : "0 0 1 1",
27775 // axisxlabels : xdata
27780 load : function(graphtype,xdata,opts)
27782 this.raphael.clear();
27784 graphtype = this.graphtype;
27789 var r = this.raphael,
27790 fin = function () {
27791 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27793 fout = function () {
27794 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27796 pfin = function() {
27797 this.sector.stop();
27798 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27801 this.label[0].stop();
27802 this.label[0].attr({ r: 7.5 });
27803 this.label[1].attr({ "font-weight": 800 });
27806 pfout = function() {
27807 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27810 this.label[0].animate({ r: 5 }, 500, "bounce");
27811 this.label[1].attr({ "font-weight": 400 });
27817 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27820 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27823 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27824 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27826 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27833 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27838 setTitle: function(o)
27843 initEvents: function() {
27846 this.el.on('click', this.onClick, this);
27850 onClick : function(e)
27852 Roo.log('img onclick');
27853 this.fireEvent('click', this, e);
27865 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27868 * @class Roo.bootstrap.dash.NumberBox
27869 * @extends Roo.bootstrap.Component
27870 * Bootstrap NumberBox class
27871 * @cfg {String} headline Box headline
27872 * @cfg {String} content Box content
27873 * @cfg {String} icon Box icon
27874 * @cfg {String} footer Footer text
27875 * @cfg {String} fhref Footer href
27878 * Create a new NumberBox
27879 * @param {Object} config The config object
27883 Roo.bootstrap.dash.NumberBox = function(config){
27884 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27888 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27897 getAutoCreate : function(){
27901 cls : 'small-box ',
27909 cls : 'roo-headline',
27910 html : this.headline
27914 cls : 'roo-content',
27915 html : this.content
27929 cls : 'ion ' + this.icon
27938 cls : 'small-box-footer',
27939 href : this.fhref || '#',
27943 cfg.cn.push(footer);
27950 onRender : function(ct,position){
27951 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27958 setHeadline: function (value)
27960 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27963 setFooter: function (value, href)
27965 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27968 this.el.select('a.small-box-footer',true).first().attr('href', href);
27973 setContent: function (value)
27975 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27978 initEvents: function()
27992 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27995 * @class Roo.bootstrap.dash.TabBox
27996 * @extends Roo.bootstrap.Component
27997 * Bootstrap TabBox class
27998 * @cfg {String} title Title of the TabBox
27999 * @cfg {String} icon Icon of the TabBox
28000 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28001 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28004 * Create a new TabBox
28005 * @param {Object} config The config object
28009 Roo.bootstrap.dash.TabBox = function(config){
28010 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28015 * When a pane is added
28016 * @param {Roo.bootstrap.dash.TabPane} pane
28020 * @event activatepane
28021 * When a pane is activated
28022 * @param {Roo.bootstrap.dash.TabPane} pane
28024 "activatepane" : true
28032 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28037 tabScrollable : false,
28039 getChildContainer : function()
28041 return this.el.select('.tab-content', true).first();
28044 getAutoCreate : function(){
28048 cls: 'pull-left header',
28056 cls: 'fa ' + this.icon
28062 cls: 'nav nav-tabs pull-right',
28068 if(this.tabScrollable){
28075 cls: 'nav nav-tabs pull-right',
28086 cls: 'nav-tabs-custom',
28091 cls: 'tab-content no-padding',
28099 initEvents : function()
28101 //Roo.log('add add pane handler');
28102 this.on('addpane', this.onAddPane, this);
28105 * Updates the box title
28106 * @param {String} html to set the title to.
28108 setTitle : function(value)
28110 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28112 onAddPane : function(pane)
28114 this.panes.push(pane);
28115 //Roo.log('addpane');
28117 // tabs are rendere left to right..
28118 if(!this.showtabs){
28122 var ctr = this.el.select('.nav-tabs', true).first();
28125 var existing = ctr.select('.nav-tab',true);
28126 var qty = existing.getCount();;
28129 var tab = ctr.createChild({
28131 cls : 'nav-tab' + (qty ? '' : ' active'),
28139 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28142 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28144 pane.el.addClass('active');
28149 onTabClick : function(ev,un,ob,pane)
28151 //Roo.log('tab - prev default');
28152 ev.preventDefault();
28155 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28156 pane.tab.addClass('active');
28157 //Roo.log(pane.title);
28158 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28159 // technically we should have a deactivate event.. but maybe add later.
28160 // and it should not de-activate the selected tab...
28161 this.fireEvent('activatepane', pane);
28162 pane.el.addClass('active');
28163 pane.fireEvent('activate');
28168 getActivePane : function()
28171 Roo.each(this.panes, function(p) {
28172 if(p.el.hasClass('active')){
28193 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28195 * @class Roo.bootstrap.TabPane
28196 * @extends Roo.bootstrap.Component
28197 * Bootstrap TabPane class
28198 * @cfg {Boolean} active (false | true) Default false
28199 * @cfg {String} title title of panel
28203 * Create a new TabPane
28204 * @param {Object} config The config object
28207 Roo.bootstrap.dash.TabPane = function(config){
28208 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28214 * When a pane is activated
28215 * @param {Roo.bootstrap.dash.TabPane} pane
28222 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28227 // the tabBox that this is attached to.
28230 getAutoCreate : function()
28238 cfg.cls += ' active';
28243 initEvents : function()
28245 //Roo.log('trigger add pane handler');
28246 this.parent().fireEvent('addpane', this)
28250 * Updates the tab title
28251 * @param {String} html to set the title to.
28253 setTitle: function(str)
28259 this.tab.select('a', true).first().dom.innerHTML = str;
28276 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28279 * @class Roo.bootstrap.menu.Menu
28280 * @extends Roo.bootstrap.Component
28281 * Bootstrap Menu class - container for Menu
28282 * @cfg {String} html Text of the menu
28283 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28284 * @cfg {String} icon Font awesome icon
28285 * @cfg {String} pos Menu align to (top | bottom) default bottom
28289 * Create a new Menu
28290 * @param {Object} config The config object
28294 Roo.bootstrap.menu.Menu = function(config){
28295 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28299 * @event beforeshow
28300 * Fires before this menu is displayed
28301 * @param {Roo.bootstrap.menu.Menu} this
28305 * @event beforehide
28306 * Fires before this menu is hidden
28307 * @param {Roo.bootstrap.menu.Menu} this
28312 * Fires after this menu is displayed
28313 * @param {Roo.bootstrap.menu.Menu} this
28318 * Fires after this menu is hidden
28319 * @param {Roo.bootstrap.menu.Menu} this
28324 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28325 * @param {Roo.bootstrap.menu.Menu} this
28326 * @param {Roo.EventObject} e
28333 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28337 weight : 'default',
28342 getChildContainer : function() {
28343 if(this.isSubMenu){
28347 return this.el.select('ul.dropdown-menu', true).first();
28350 getAutoCreate : function()
28355 cls : 'roo-menu-text',
28363 cls : 'fa ' + this.icon
28374 cls : 'dropdown-button btn btn-' + this.weight,
28379 cls : 'dropdown-toggle btn btn-' + this.weight,
28389 cls : 'dropdown-menu'
28395 if(this.pos == 'top'){
28396 cfg.cls += ' dropup';
28399 if(this.isSubMenu){
28402 cls : 'dropdown-menu'
28409 onRender : function(ct, position)
28411 this.isSubMenu = ct.hasClass('dropdown-submenu');
28413 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28416 initEvents : function()
28418 if(this.isSubMenu){
28422 this.hidden = true;
28424 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28425 this.triggerEl.on('click', this.onTriggerPress, this);
28427 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28428 this.buttonEl.on('click', this.onClick, this);
28434 if(this.isSubMenu){
28438 return this.el.select('ul.dropdown-menu', true).first();
28441 onClick : function(e)
28443 this.fireEvent("click", this, e);
28446 onTriggerPress : function(e)
28448 if (this.isVisible()) {
28455 isVisible : function(){
28456 return !this.hidden;
28461 this.fireEvent("beforeshow", this);
28463 this.hidden = false;
28464 this.el.addClass('open');
28466 Roo.get(document).on("mouseup", this.onMouseUp, this);
28468 this.fireEvent("show", this);
28475 this.fireEvent("beforehide", this);
28477 this.hidden = true;
28478 this.el.removeClass('open');
28480 Roo.get(document).un("mouseup", this.onMouseUp);
28482 this.fireEvent("hide", this);
28485 onMouseUp : function()
28499 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28502 * @class Roo.bootstrap.menu.Item
28503 * @extends Roo.bootstrap.Component
28504 * Bootstrap MenuItem class
28505 * @cfg {Boolean} submenu (true | false) default false
28506 * @cfg {String} html text of the item
28507 * @cfg {String} href the link
28508 * @cfg {Boolean} disable (true | false) default false
28509 * @cfg {Boolean} preventDefault (true | false) default true
28510 * @cfg {String} icon Font awesome icon
28511 * @cfg {String} pos Submenu align to (left | right) default right
28515 * Create a new Item
28516 * @param {Object} config The config object
28520 Roo.bootstrap.menu.Item = function(config){
28521 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28525 * Fires when the mouse is hovering over this menu
28526 * @param {Roo.bootstrap.menu.Item} this
28527 * @param {Roo.EventObject} e
28532 * Fires when the mouse exits this menu
28533 * @param {Roo.bootstrap.menu.Item} this
28534 * @param {Roo.EventObject} e
28540 * The raw click event for the entire grid.
28541 * @param {Roo.EventObject} e
28547 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28552 preventDefault: true,
28557 getAutoCreate : function()
28562 cls : 'roo-menu-item-text',
28570 cls : 'fa ' + this.icon
28579 href : this.href || '#',
28586 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28590 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28592 if(this.pos == 'left'){
28593 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28600 initEvents : function()
28602 this.el.on('mouseover', this.onMouseOver, this);
28603 this.el.on('mouseout', this.onMouseOut, this);
28605 this.el.select('a', true).first().on('click', this.onClick, this);
28609 onClick : function(e)
28611 if(this.preventDefault){
28612 e.preventDefault();
28615 this.fireEvent("click", this, e);
28618 onMouseOver : function(e)
28620 if(this.submenu && this.pos == 'left'){
28621 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28624 this.fireEvent("mouseover", this, e);
28627 onMouseOut : function(e)
28629 this.fireEvent("mouseout", this, e);
28641 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28644 * @class Roo.bootstrap.menu.Separator
28645 * @extends Roo.bootstrap.Component
28646 * Bootstrap Separator class
28649 * Create a new Separator
28650 * @param {Object} config The config object
28654 Roo.bootstrap.menu.Separator = function(config){
28655 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28658 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28660 getAutoCreate : function(){
28681 * @class Roo.bootstrap.Tooltip
28682 * Bootstrap Tooltip class
28683 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28684 * to determine which dom element triggers the tooltip.
28686 * It needs to add support for additional attributes like tooltip-position
28689 * Create a new Toolti
28690 * @param {Object} config The config object
28693 Roo.bootstrap.Tooltip = function(config){
28694 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28696 this.alignment = Roo.bootstrap.Tooltip.alignment;
28698 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28699 this.alignment = config.alignment;
28704 Roo.apply(Roo.bootstrap.Tooltip, {
28706 * @function init initialize tooltip monitoring.
28710 currentTip : false,
28711 currentRegion : false,
28717 Roo.get(document).on('mouseover', this.enter ,this);
28718 Roo.get(document).on('mouseout', this.leave, this);
28721 this.currentTip = new Roo.bootstrap.Tooltip();
28724 enter : function(ev)
28726 var dom = ev.getTarget();
28728 //Roo.log(['enter',dom]);
28729 var el = Roo.fly(dom);
28730 if (this.currentEl) {
28732 //Roo.log(this.currentEl);
28733 //Roo.log(this.currentEl.contains(dom));
28734 if (this.currentEl == el) {
28737 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28743 if (this.currentTip.el) {
28744 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28748 if(!el || el.dom == document){
28754 // you can not look for children, as if el is the body.. then everythign is the child..
28755 if (!el.attr('tooltip')) { //
28756 if (!el.select("[tooltip]").elements.length) {
28759 // is the mouse over this child...?
28760 bindEl = el.select("[tooltip]").first();
28761 var xy = ev.getXY();
28762 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28763 //Roo.log("not in region.");
28766 //Roo.log("child element over..");
28769 this.currentEl = bindEl;
28770 this.currentTip.bind(bindEl);
28771 this.currentRegion = Roo.lib.Region.getRegion(dom);
28772 this.currentTip.enter();
28775 leave : function(ev)
28777 var dom = ev.getTarget();
28778 //Roo.log(['leave',dom]);
28779 if (!this.currentEl) {
28784 if (dom != this.currentEl.dom) {
28787 var xy = ev.getXY();
28788 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28791 // only activate leave if mouse cursor is outside... bounding box..
28796 if (this.currentTip) {
28797 this.currentTip.leave();
28799 //Roo.log('clear currentEl');
28800 this.currentEl = false;
28805 'left' : ['r-l', [-2,0], 'right'],
28806 'right' : ['l-r', [2,0], 'left'],
28807 'bottom' : ['t-b', [0,2], 'top'],
28808 'top' : [ 'b-t', [0,-2], 'bottom']
28814 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28819 delay : null, // can be { show : 300 , hide: 500}
28823 hoverState : null, //???
28825 placement : 'bottom',
28829 getAutoCreate : function(){
28836 cls : 'tooltip-arrow arrow'
28839 cls : 'tooltip-inner'
28846 bind : function(el)
28851 initEvents : function()
28853 this.arrowEl = this.el.select('.arrow', true).first();
28854 this.innerEl = this.el.select('.tooltip-inner', true).first();
28857 enter : function () {
28859 if (this.timeout != null) {
28860 clearTimeout(this.timeout);
28863 this.hoverState = 'in';
28864 //Roo.log("enter - show");
28865 if (!this.delay || !this.delay.show) {
28870 this.timeout = setTimeout(function () {
28871 if (_t.hoverState == 'in') {
28874 }, this.delay.show);
28878 clearTimeout(this.timeout);
28880 this.hoverState = 'out';
28881 if (!this.delay || !this.delay.hide) {
28887 this.timeout = setTimeout(function () {
28888 //Roo.log("leave - timeout");
28890 if (_t.hoverState == 'out') {
28892 Roo.bootstrap.Tooltip.currentEl = false;
28897 show : function (msg)
28900 this.render(document.body);
28903 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28905 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28907 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28909 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28910 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28912 var placement = typeof this.placement == 'function' ?
28913 this.placement.call(this, this.el, on_el) :
28916 var autoToken = /\s?auto?\s?/i;
28917 var autoPlace = autoToken.test(placement);
28919 placement = placement.replace(autoToken, '') || 'top';
28923 //this.el.setXY([0,0]);
28925 //this.el.dom.style.display='block';
28927 //this.el.appendTo(on_el);
28929 var p = this.getPosition();
28930 var box = this.el.getBox();
28936 var align = this.alignment[placement];
28938 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28940 if(placement == 'top' || placement == 'bottom'){
28942 placement = 'right';
28945 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28946 placement = 'left';
28949 var scroll = Roo.select('body', true).first().getScroll();
28951 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28955 align = this.alignment[placement];
28957 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28961 this.el.alignTo(this.bindEl, align[0],align[1]);
28962 //var arrow = this.el.select('.arrow',true).first();
28963 //arrow.set(align[2],
28965 this.el.addClass(placement);
28966 this.el.addClass("bs-tooltip-"+ placement);
28968 this.el.addClass('in fade show');
28970 this.hoverState = null;
28972 if (this.el.hasClass('fade')) {
28987 //this.el.setXY([0,0]);
28988 this.el.removeClass(['show', 'in']);
29004 * @class Roo.bootstrap.LocationPicker
29005 * @extends Roo.bootstrap.Component
29006 * Bootstrap LocationPicker class
29007 * @cfg {Number} latitude Position when init default 0
29008 * @cfg {Number} longitude Position when init default 0
29009 * @cfg {Number} zoom default 15
29010 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29011 * @cfg {Boolean} mapTypeControl default false
29012 * @cfg {Boolean} disableDoubleClickZoom default false
29013 * @cfg {Boolean} scrollwheel default true
29014 * @cfg {Boolean} streetViewControl default false
29015 * @cfg {Number} radius default 0
29016 * @cfg {String} locationName
29017 * @cfg {Boolean} draggable default true
29018 * @cfg {Boolean} enableAutocomplete default false
29019 * @cfg {Boolean} enableReverseGeocode default true
29020 * @cfg {String} markerTitle
29023 * Create a new LocationPicker
29024 * @param {Object} config The config object
29028 Roo.bootstrap.LocationPicker = function(config){
29030 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29035 * Fires when the picker initialized.
29036 * @param {Roo.bootstrap.LocationPicker} this
29037 * @param {Google Location} location
29041 * @event positionchanged
29042 * Fires when the picker position changed.
29043 * @param {Roo.bootstrap.LocationPicker} this
29044 * @param {Google Location} location
29046 positionchanged : true,
29049 * Fires when the map resize.
29050 * @param {Roo.bootstrap.LocationPicker} this
29055 * Fires when the map show.
29056 * @param {Roo.bootstrap.LocationPicker} this
29061 * Fires when the map hide.
29062 * @param {Roo.bootstrap.LocationPicker} this
29067 * Fires when click the map.
29068 * @param {Roo.bootstrap.LocationPicker} this
29069 * @param {Map event} e
29073 * @event mapRightClick
29074 * Fires when right click the map.
29075 * @param {Roo.bootstrap.LocationPicker} this
29076 * @param {Map event} e
29078 mapRightClick : true,
29080 * @event markerClick
29081 * Fires when click the marker.
29082 * @param {Roo.bootstrap.LocationPicker} this
29083 * @param {Map event} e
29085 markerClick : true,
29087 * @event markerRightClick
29088 * Fires when right click the marker.
29089 * @param {Roo.bootstrap.LocationPicker} this
29090 * @param {Map event} e
29092 markerRightClick : true,
29094 * @event OverlayViewDraw
29095 * Fires when OverlayView Draw
29096 * @param {Roo.bootstrap.LocationPicker} this
29098 OverlayViewDraw : true,
29100 * @event OverlayViewOnAdd
29101 * Fires when OverlayView Draw
29102 * @param {Roo.bootstrap.LocationPicker} this
29104 OverlayViewOnAdd : true,
29106 * @event OverlayViewOnRemove
29107 * Fires when OverlayView Draw
29108 * @param {Roo.bootstrap.LocationPicker} this
29110 OverlayViewOnRemove : true,
29112 * @event OverlayViewShow
29113 * Fires when OverlayView Draw
29114 * @param {Roo.bootstrap.LocationPicker} this
29115 * @param {Pixel} cpx
29117 OverlayViewShow : true,
29119 * @event OverlayViewHide
29120 * Fires when OverlayView Draw
29121 * @param {Roo.bootstrap.LocationPicker} this
29123 OverlayViewHide : true,
29125 * @event loadexception
29126 * Fires when load google lib failed.
29127 * @param {Roo.bootstrap.LocationPicker} this
29129 loadexception : true
29134 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29136 gMapContext: false,
29142 mapTypeControl: false,
29143 disableDoubleClickZoom: false,
29145 streetViewControl: false,
29149 enableAutocomplete: false,
29150 enableReverseGeocode: true,
29153 getAutoCreate: function()
29158 cls: 'roo-location-picker'
29164 initEvents: function(ct, position)
29166 if(!this.el.getWidth() || this.isApplied()){
29170 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29175 initial: function()
29177 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29178 this.fireEvent('loadexception', this);
29182 if(!this.mapTypeId){
29183 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29186 this.gMapContext = this.GMapContext();
29188 this.initOverlayView();
29190 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29194 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29195 _this.setPosition(_this.gMapContext.marker.position);
29198 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29199 _this.fireEvent('mapClick', this, event);
29203 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29204 _this.fireEvent('mapRightClick', this, event);
29208 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29209 _this.fireEvent('markerClick', this, event);
29213 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29214 _this.fireEvent('markerRightClick', this, event);
29218 this.setPosition(this.gMapContext.location);
29220 this.fireEvent('initial', this, this.gMapContext.location);
29223 initOverlayView: function()
29227 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29231 _this.fireEvent('OverlayViewDraw', _this);
29236 _this.fireEvent('OverlayViewOnAdd', _this);
29239 onRemove: function()
29241 _this.fireEvent('OverlayViewOnRemove', _this);
29244 show: function(cpx)
29246 _this.fireEvent('OverlayViewShow', _this, cpx);
29251 _this.fireEvent('OverlayViewHide', _this);
29257 fromLatLngToContainerPixel: function(event)
29259 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29262 isApplied: function()
29264 return this.getGmapContext() == false ? false : true;
29267 getGmapContext: function()
29269 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29272 GMapContext: function()
29274 var position = new google.maps.LatLng(this.latitude, this.longitude);
29276 var _map = new google.maps.Map(this.el.dom, {
29279 mapTypeId: this.mapTypeId,
29280 mapTypeControl: this.mapTypeControl,
29281 disableDoubleClickZoom: this.disableDoubleClickZoom,
29282 scrollwheel: this.scrollwheel,
29283 streetViewControl: this.streetViewControl,
29284 locationName: this.locationName,
29285 draggable: this.draggable,
29286 enableAutocomplete: this.enableAutocomplete,
29287 enableReverseGeocode: this.enableReverseGeocode
29290 var _marker = new google.maps.Marker({
29291 position: position,
29293 title: this.markerTitle,
29294 draggable: this.draggable
29301 location: position,
29302 radius: this.radius,
29303 locationName: this.locationName,
29304 addressComponents: {
29305 formatted_address: null,
29306 addressLine1: null,
29307 addressLine2: null,
29309 streetNumber: null,
29313 stateOrProvince: null
29316 domContainer: this.el.dom,
29317 geodecoder: new google.maps.Geocoder()
29321 drawCircle: function(center, radius, options)
29323 if (this.gMapContext.circle != null) {
29324 this.gMapContext.circle.setMap(null);
29328 options = Roo.apply({}, options, {
29329 strokeColor: "#0000FF",
29330 strokeOpacity: .35,
29332 fillColor: "#0000FF",
29336 options.map = this.gMapContext.map;
29337 options.radius = radius;
29338 options.center = center;
29339 this.gMapContext.circle = new google.maps.Circle(options);
29340 return this.gMapContext.circle;
29346 setPosition: function(location)
29348 this.gMapContext.location = location;
29349 this.gMapContext.marker.setPosition(location);
29350 this.gMapContext.map.panTo(location);
29351 this.drawCircle(location, this.gMapContext.radius, {});
29355 if (this.gMapContext.settings.enableReverseGeocode) {
29356 this.gMapContext.geodecoder.geocode({
29357 latLng: this.gMapContext.location
29358 }, function(results, status) {
29360 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29361 _this.gMapContext.locationName = results[0].formatted_address;
29362 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29364 _this.fireEvent('positionchanged', this, location);
29371 this.fireEvent('positionchanged', this, location);
29376 google.maps.event.trigger(this.gMapContext.map, "resize");
29378 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29380 this.fireEvent('resize', this);
29383 setPositionByLatLng: function(latitude, longitude)
29385 this.setPosition(new google.maps.LatLng(latitude, longitude));
29388 getCurrentPosition: function()
29391 latitude: this.gMapContext.location.lat(),
29392 longitude: this.gMapContext.location.lng()
29396 getAddressName: function()
29398 return this.gMapContext.locationName;
29401 getAddressComponents: function()
29403 return this.gMapContext.addressComponents;
29406 address_component_from_google_geocode: function(address_components)
29410 for (var i = 0; i < address_components.length; i++) {
29411 var component = address_components[i];
29412 if (component.types.indexOf("postal_code") >= 0) {
29413 result.postalCode = component.short_name;
29414 } else if (component.types.indexOf("street_number") >= 0) {
29415 result.streetNumber = component.short_name;
29416 } else if (component.types.indexOf("route") >= 0) {
29417 result.streetName = component.short_name;
29418 } else if (component.types.indexOf("neighborhood") >= 0) {
29419 result.city = component.short_name;
29420 } else if (component.types.indexOf("locality") >= 0) {
29421 result.city = component.short_name;
29422 } else if (component.types.indexOf("sublocality") >= 0) {
29423 result.district = component.short_name;
29424 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29425 result.stateOrProvince = component.short_name;
29426 } else if (component.types.indexOf("country") >= 0) {
29427 result.country = component.short_name;
29431 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29432 result.addressLine2 = "";
29436 setZoomLevel: function(zoom)
29438 this.gMapContext.map.setZoom(zoom);
29451 this.fireEvent('show', this);
29462 this.fireEvent('hide', this);
29467 Roo.apply(Roo.bootstrap.LocationPicker, {
29469 OverlayView : function(map, options)
29471 options = options || {};
29478 * @class Roo.bootstrap.Alert
29479 * @extends Roo.bootstrap.Component
29480 * Bootstrap Alert class - shows an alert area box
29482 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29483 Enter a valid email address
29486 * @cfg {String} title The title of alert
29487 * @cfg {String} html The content of alert
29488 * @cfg {String} weight ( success | info | warning | danger )
29489 * @cfg {String} faicon font-awesomeicon
29492 * Create a new alert
29493 * @param {Object} config The config object
29497 Roo.bootstrap.Alert = function(config){
29498 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29502 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29509 getAutoCreate : function()
29518 cls : 'roo-alert-icon'
29523 cls : 'roo-alert-title',
29528 cls : 'roo-alert-text',
29535 cfg.cn[0].cls += ' fa ' + this.faicon;
29539 cfg.cls += ' alert-' + this.weight;
29545 initEvents: function()
29547 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29550 setTitle : function(str)
29552 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29555 setText : function(str)
29557 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29560 setWeight : function(weight)
29563 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29566 this.weight = weight;
29568 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29571 setIcon : function(icon)
29574 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29577 this.faicon = icon;
29579 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29600 * @class Roo.bootstrap.UploadCropbox
29601 * @extends Roo.bootstrap.Component
29602 * Bootstrap UploadCropbox class
29603 * @cfg {String} emptyText show when image has been loaded
29604 * @cfg {String} rotateNotify show when image too small to rotate
29605 * @cfg {Number} errorTimeout default 3000
29606 * @cfg {Number} minWidth default 300
29607 * @cfg {Number} minHeight default 300
29608 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29609 * @cfg {Boolean} isDocument (true|false) default false
29610 * @cfg {String} url action url
29611 * @cfg {String} paramName default 'imageUpload'
29612 * @cfg {String} method default POST
29613 * @cfg {Boolean} loadMask (true|false) default true
29614 * @cfg {Boolean} loadingText default 'Loading...'
29617 * Create a new UploadCropbox
29618 * @param {Object} config The config object
29621 Roo.bootstrap.UploadCropbox = function(config){
29622 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29626 * @event beforeselectfile
29627 * Fire before select file
29628 * @param {Roo.bootstrap.UploadCropbox} this
29630 "beforeselectfile" : true,
29633 * Fire after initEvent
29634 * @param {Roo.bootstrap.UploadCropbox} this
29639 * Fire after initEvent
29640 * @param {Roo.bootstrap.UploadCropbox} this
29641 * @param {String} data
29646 * Fire when preparing the file data
29647 * @param {Roo.bootstrap.UploadCropbox} this
29648 * @param {Object} file
29653 * Fire when get exception
29654 * @param {Roo.bootstrap.UploadCropbox} this
29655 * @param {XMLHttpRequest} xhr
29657 "exception" : true,
29659 * @event beforeloadcanvas
29660 * Fire before load the canvas
29661 * @param {Roo.bootstrap.UploadCropbox} this
29662 * @param {String} src
29664 "beforeloadcanvas" : true,
29667 * Fire when trash image
29668 * @param {Roo.bootstrap.UploadCropbox} this
29673 * Fire when download the image
29674 * @param {Roo.bootstrap.UploadCropbox} this
29678 * @event footerbuttonclick
29679 * Fire when footerbuttonclick
29680 * @param {Roo.bootstrap.UploadCropbox} this
29681 * @param {String} type
29683 "footerbuttonclick" : true,
29687 * @param {Roo.bootstrap.UploadCropbox} this
29692 * Fire when rotate the image
29693 * @param {Roo.bootstrap.UploadCropbox} this
29694 * @param {String} pos
29699 * Fire when inspect the file
29700 * @param {Roo.bootstrap.UploadCropbox} this
29701 * @param {Object} file
29706 * Fire when xhr upload the file
29707 * @param {Roo.bootstrap.UploadCropbox} this
29708 * @param {Object} data
29713 * Fire when arrange the file data
29714 * @param {Roo.bootstrap.UploadCropbox} this
29715 * @param {Object} formData
29720 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29723 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29725 emptyText : 'Click to upload image',
29726 rotateNotify : 'Image is too small to rotate',
29727 errorTimeout : 3000,
29741 cropType : 'image/jpeg',
29743 canvasLoaded : false,
29744 isDocument : false,
29746 paramName : 'imageUpload',
29748 loadingText : 'Loading...',
29751 getAutoCreate : function()
29755 cls : 'roo-upload-cropbox',
29759 cls : 'roo-upload-cropbox-selector',
29764 cls : 'roo-upload-cropbox-body',
29765 style : 'cursor:pointer',
29769 cls : 'roo-upload-cropbox-preview'
29773 cls : 'roo-upload-cropbox-thumb'
29777 cls : 'roo-upload-cropbox-empty-notify',
29778 html : this.emptyText
29782 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29783 html : this.rotateNotify
29789 cls : 'roo-upload-cropbox-footer',
29792 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29802 onRender : function(ct, position)
29804 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29806 if (this.buttons.length) {
29808 Roo.each(this.buttons, function(bb) {
29810 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29812 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29818 this.maskEl = this.el;
29822 initEvents : function()
29824 this.urlAPI = (window.createObjectURL && window) ||
29825 (window.URL && URL.revokeObjectURL && URL) ||
29826 (window.webkitURL && webkitURL);
29828 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29829 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29831 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29832 this.selectorEl.hide();
29834 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29835 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29837 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29838 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29839 this.thumbEl.hide();
29841 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29842 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29844 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29845 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29846 this.errorEl.hide();
29848 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29849 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29850 this.footerEl.hide();
29852 this.setThumbBoxSize();
29858 this.fireEvent('initial', this);
29865 window.addEventListener("resize", function() { _this.resize(); } );
29867 this.bodyEl.on('click', this.beforeSelectFile, this);
29870 this.bodyEl.on('touchstart', this.onTouchStart, this);
29871 this.bodyEl.on('touchmove', this.onTouchMove, this);
29872 this.bodyEl.on('touchend', this.onTouchEnd, this);
29876 this.bodyEl.on('mousedown', this.onMouseDown, this);
29877 this.bodyEl.on('mousemove', this.onMouseMove, this);
29878 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29879 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29880 Roo.get(document).on('mouseup', this.onMouseUp, this);
29883 this.selectorEl.on('change', this.onFileSelected, this);
29889 this.baseScale = 1;
29891 this.baseRotate = 1;
29892 this.dragable = false;
29893 this.pinching = false;
29896 this.cropData = false;
29897 this.notifyEl.dom.innerHTML = this.emptyText;
29899 this.selectorEl.dom.value = '';
29903 resize : function()
29905 if(this.fireEvent('resize', this) != false){
29906 this.setThumbBoxPosition();
29907 this.setCanvasPosition();
29911 onFooterButtonClick : function(e, el, o, type)
29914 case 'rotate-left' :
29915 this.onRotateLeft(e);
29917 case 'rotate-right' :
29918 this.onRotateRight(e);
29921 this.beforeSelectFile(e);
29936 this.fireEvent('footerbuttonclick', this, type);
29939 beforeSelectFile : function(e)
29941 e.preventDefault();
29943 if(this.fireEvent('beforeselectfile', this) != false){
29944 this.selectorEl.dom.click();
29948 onFileSelected : function(e)
29950 e.preventDefault();
29952 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29956 var file = this.selectorEl.dom.files[0];
29958 if(this.fireEvent('inspect', this, file) != false){
29959 this.prepare(file);
29964 trash : function(e)
29966 this.fireEvent('trash', this);
29969 download : function(e)
29971 this.fireEvent('download', this);
29974 loadCanvas : function(src)
29976 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29980 this.imageEl = document.createElement('img');
29984 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29986 this.imageEl.src = src;
29990 onLoadCanvas : function()
29992 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29993 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29995 this.bodyEl.un('click', this.beforeSelectFile, this);
29997 this.notifyEl.hide();
29998 this.thumbEl.show();
29999 this.footerEl.show();
30001 this.baseRotateLevel();
30003 if(this.isDocument){
30004 this.setThumbBoxSize();
30007 this.setThumbBoxPosition();
30009 this.baseScaleLevel();
30015 this.canvasLoaded = true;
30018 this.maskEl.unmask();
30023 setCanvasPosition : function()
30025 if(!this.canvasEl){
30029 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30030 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30032 this.previewEl.setLeft(pw);
30033 this.previewEl.setTop(ph);
30037 onMouseDown : function(e)
30041 this.dragable = true;
30042 this.pinching = false;
30044 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30045 this.dragable = false;
30049 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30050 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30054 onMouseMove : function(e)
30058 if(!this.canvasLoaded){
30062 if (!this.dragable){
30066 var minX = Math.ceil(this.thumbEl.getLeft(true));
30067 var minY = Math.ceil(this.thumbEl.getTop(true));
30069 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30070 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30072 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30073 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30075 x = x - this.mouseX;
30076 y = y - this.mouseY;
30078 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30079 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30081 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30082 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30084 this.previewEl.setLeft(bgX);
30085 this.previewEl.setTop(bgY);
30087 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30088 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30091 onMouseUp : function(e)
30095 this.dragable = false;
30098 onMouseWheel : function(e)
30102 this.startScale = this.scale;
30104 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30106 if(!this.zoomable()){
30107 this.scale = this.startScale;
30116 zoomable : function()
30118 var minScale = this.thumbEl.getWidth() / this.minWidth;
30120 if(this.minWidth < this.minHeight){
30121 minScale = this.thumbEl.getHeight() / this.minHeight;
30124 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30125 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30129 (this.rotate == 0 || this.rotate == 180) &&
30131 width > this.imageEl.OriginWidth ||
30132 height > this.imageEl.OriginHeight ||
30133 (width < this.minWidth && height < this.minHeight)
30141 (this.rotate == 90 || this.rotate == 270) &&
30143 width > this.imageEl.OriginWidth ||
30144 height > this.imageEl.OriginHeight ||
30145 (width < this.minHeight && height < this.minWidth)
30152 !this.isDocument &&
30153 (this.rotate == 0 || this.rotate == 180) &&
30155 width < this.minWidth ||
30156 width > this.imageEl.OriginWidth ||
30157 height < this.minHeight ||
30158 height > this.imageEl.OriginHeight
30165 !this.isDocument &&
30166 (this.rotate == 90 || this.rotate == 270) &&
30168 width < this.minHeight ||
30169 width > this.imageEl.OriginWidth ||
30170 height < this.minWidth ||
30171 height > this.imageEl.OriginHeight
30181 onRotateLeft : function(e)
30183 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30185 var minScale = this.thumbEl.getWidth() / this.minWidth;
30187 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30188 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30190 this.startScale = this.scale;
30192 while (this.getScaleLevel() < minScale){
30194 this.scale = this.scale + 1;
30196 if(!this.zoomable()){
30201 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30202 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30207 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30214 this.scale = this.startScale;
30216 this.onRotateFail();
30221 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30223 if(this.isDocument){
30224 this.setThumbBoxSize();
30225 this.setThumbBoxPosition();
30226 this.setCanvasPosition();
30231 this.fireEvent('rotate', this, 'left');
30235 onRotateRight : function(e)
30237 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30239 var minScale = this.thumbEl.getWidth() / this.minWidth;
30241 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30242 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30244 this.startScale = this.scale;
30246 while (this.getScaleLevel() < minScale){
30248 this.scale = this.scale + 1;
30250 if(!this.zoomable()){
30255 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30256 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30261 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30268 this.scale = this.startScale;
30270 this.onRotateFail();
30275 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30277 if(this.isDocument){
30278 this.setThumbBoxSize();
30279 this.setThumbBoxPosition();
30280 this.setCanvasPosition();
30285 this.fireEvent('rotate', this, 'right');
30288 onRotateFail : function()
30290 this.errorEl.show(true);
30294 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30299 this.previewEl.dom.innerHTML = '';
30301 var canvasEl = document.createElement("canvas");
30303 var contextEl = canvasEl.getContext("2d");
30305 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30306 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30307 var center = this.imageEl.OriginWidth / 2;
30309 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30310 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30311 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30312 center = this.imageEl.OriginHeight / 2;
30315 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30317 contextEl.translate(center, center);
30318 contextEl.rotate(this.rotate * Math.PI / 180);
30320 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30322 this.canvasEl = document.createElement("canvas");
30324 this.contextEl = this.canvasEl.getContext("2d");
30326 switch (this.rotate) {
30329 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30330 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30332 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30337 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30338 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30340 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30341 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);
30345 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30350 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30351 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30353 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30354 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);
30358 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);
30363 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30364 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30366 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30367 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30371 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);
30378 this.previewEl.appendChild(this.canvasEl);
30380 this.setCanvasPosition();
30385 if(!this.canvasLoaded){
30389 var imageCanvas = document.createElement("canvas");
30391 var imageContext = imageCanvas.getContext("2d");
30393 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30394 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30396 var center = imageCanvas.width / 2;
30398 imageContext.translate(center, center);
30400 imageContext.rotate(this.rotate * Math.PI / 180);
30402 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30404 var canvas = document.createElement("canvas");
30406 var context = canvas.getContext("2d");
30408 canvas.width = this.minWidth;
30409 canvas.height = this.minHeight;
30411 switch (this.rotate) {
30414 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30415 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30417 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30418 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30420 var targetWidth = this.minWidth - 2 * x;
30421 var targetHeight = this.minHeight - 2 * y;
30425 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30426 scale = targetWidth / width;
30429 if(x > 0 && y == 0){
30430 scale = targetHeight / height;
30433 if(x > 0 && y > 0){
30434 scale = targetWidth / width;
30436 if(width < height){
30437 scale = targetHeight / height;
30441 context.scale(scale, scale);
30443 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30444 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30446 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30447 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30449 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30454 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30455 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30457 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30458 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30460 var targetWidth = this.minWidth - 2 * x;
30461 var targetHeight = this.minHeight - 2 * y;
30465 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30466 scale = targetWidth / width;
30469 if(x > 0 && y == 0){
30470 scale = targetHeight / height;
30473 if(x > 0 && y > 0){
30474 scale = targetWidth / width;
30476 if(width < height){
30477 scale = targetHeight / height;
30481 context.scale(scale, scale);
30483 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30484 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30486 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30487 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30489 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30491 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30496 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30497 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30499 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30500 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30502 var targetWidth = this.minWidth - 2 * x;
30503 var targetHeight = this.minHeight - 2 * y;
30507 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30508 scale = targetWidth / width;
30511 if(x > 0 && y == 0){
30512 scale = targetHeight / height;
30515 if(x > 0 && y > 0){
30516 scale = targetWidth / width;
30518 if(width < height){
30519 scale = targetHeight / height;
30523 context.scale(scale, scale);
30525 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30526 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30528 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30529 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30531 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30532 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30534 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30539 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30540 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30542 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30543 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30545 var targetWidth = this.minWidth - 2 * x;
30546 var targetHeight = this.minHeight - 2 * y;
30550 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30551 scale = targetWidth / width;
30554 if(x > 0 && y == 0){
30555 scale = targetHeight / height;
30558 if(x > 0 && y > 0){
30559 scale = targetWidth / width;
30561 if(width < height){
30562 scale = targetHeight / height;
30566 context.scale(scale, scale);
30568 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30569 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30571 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30572 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30574 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30576 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30583 this.cropData = canvas.toDataURL(this.cropType);
30585 if(this.fireEvent('crop', this, this.cropData) !== false){
30586 this.process(this.file, this.cropData);
30593 setThumbBoxSize : function()
30597 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30598 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30599 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30601 this.minWidth = width;
30602 this.minHeight = height;
30604 if(this.rotate == 90 || this.rotate == 270){
30605 this.minWidth = height;
30606 this.minHeight = width;
30611 width = Math.ceil(this.minWidth * height / this.minHeight);
30613 if(this.minWidth > this.minHeight){
30615 height = Math.ceil(this.minHeight * width / this.minWidth);
30618 this.thumbEl.setStyle({
30619 width : width + 'px',
30620 height : height + 'px'
30627 setThumbBoxPosition : function()
30629 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30630 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30632 this.thumbEl.setLeft(x);
30633 this.thumbEl.setTop(y);
30637 baseRotateLevel : function()
30639 this.baseRotate = 1;
30642 typeof(this.exif) != 'undefined' &&
30643 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30644 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30646 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30649 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30653 baseScaleLevel : function()
30657 if(this.isDocument){
30659 if(this.baseRotate == 6 || this.baseRotate == 8){
30661 height = this.thumbEl.getHeight();
30662 this.baseScale = height / this.imageEl.OriginWidth;
30664 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30665 width = this.thumbEl.getWidth();
30666 this.baseScale = width / this.imageEl.OriginHeight;
30672 height = this.thumbEl.getHeight();
30673 this.baseScale = height / this.imageEl.OriginHeight;
30675 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30676 width = this.thumbEl.getWidth();
30677 this.baseScale = width / this.imageEl.OriginWidth;
30683 if(this.baseRotate == 6 || this.baseRotate == 8){
30685 width = this.thumbEl.getHeight();
30686 this.baseScale = width / this.imageEl.OriginHeight;
30688 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30689 height = this.thumbEl.getWidth();
30690 this.baseScale = height / this.imageEl.OriginHeight;
30693 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30694 height = this.thumbEl.getWidth();
30695 this.baseScale = height / this.imageEl.OriginHeight;
30697 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30698 width = this.thumbEl.getHeight();
30699 this.baseScale = width / this.imageEl.OriginWidth;
30706 width = this.thumbEl.getWidth();
30707 this.baseScale = width / this.imageEl.OriginWidth;
30709 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30710 height = this.thumbEl.getHeight();
30711 this.baseScale = height / this.imageEl.OriginHeight;
30714 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30716 height = this.thumbEl.getHeight();
30717 this.baseScale = height / this.imageEl.OriginHeight;
30719 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30720 width = this.thumbEl.getWidth();
30721 this.baseScale = width / this.imageEl.OriginWidth;
30729 getScaleLevel : function()
30731 return this.baseScale * Math.pow(1.1, this.scale);
30734 onTouchStart : function(e)
30736 if(!this.canvasLoaded){
30737 this.beforeSelectFile(e);
30741 var touches = e.browserEvent.touches;
30747 if(touches.length == 1){
30748 this.onMouseDown(e);
30752 if(touches.length != 2){
30758 for(var i = 0, finger; finger = touches[i]; i++){
30759 coords.push(finger.pageX, finger.pageY);
30762 var x = Math.pow(coords[0] - coords[2], 2);
30763 var y = Math.pow(coords[1] - coords[3], 2);
30765 this.startDistance = Math.sqrt(x + y);
30767 this.startScale = this.scale;
30769 this.pinching = true;
30770 this.dragable = false;
30774 onTouchMove : function(e)
30776 if(!this.pinching && !this.dragable){
30780 var touches = e.browserEvent.touches;
30787 this.onMouseMove(e);
30793 for(var i = 0, finger; finger = touches[i]; i++){
30794 coords.push(finger.pageX, finger.pageY);
30797 var x = Math.pow(coords[0] - coords[2], 2);
30798 var y = Math.pow(coords[1] - coords[3], 2);
30800 this.endDistance = Math.sqrt(x + y);
30802 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30804 if(!this.zoomable()){
30805 this.scale = this.startScale;
30813 onTouchEnd : function(e)
30815 this.pinching = false;
30816 this.dragable = false;
30820 process : function(file, crop)
30823 this.maskEl.mask(this.loadingText);
30826 this.xhr = new XMLHttpRequest();
30828 file.xhr = this.xhr;
30830 this.xhr.open(this.method, this.url, true);
30833 "Accept": "application/json",
30834 "Cache-Control": "no-cache",
30835 "X-Requested-With": "XMLHttpRequest"
30838 for (var headerName in headers) {
30839 var headerValue = headers[headerName];
30841 this.xhr.setRequestHeader(headerName, headerValue);
30847 this.xhr.onload = function()
30849 _this.xhrOnLoad(_this.xhr);
30852 this.xhr.onerror = function()
30854 _this.xhrOnError(_this.xhr);
30857 var formData = new FormData();
30859 formData.append('returnHTML', 'NO');
30862 formData.append('crop', crop);
30865 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30866 formData.append(this.paramName, file, file.name);
30869 if(typeof(file.filename) != 'undefined'){
30870 formData.append('filename', file.filename);
30873 if(typeof(file.mimetype) != 'undefined'){
30874 formData.append('mimetype', file.mimetype);
30877 if(this.fireEvent('arrange', this, formData) != false){
30878 this.xhr.send(formData);
30882 xhrOnLoad : function(xhr)
30885 this.maskEl.unmask();
30888 if (xhr.readyState !== 4) {
30889 this.fireEvent('exception', this, xhr);
30893 var response = Roo.decode(xhr.responseText);
30895 if(!response.success){
30896 this.fireEvent('exception', this, xhr);
30900 var response = Roo.decode(xhr.responseText);
30902 this.fireEvent('upload', this, response);
30906 xhrOnError : function()
30909 this.maskEl.unmask();
30912 Roo.log('xhr on error');
30914 var response = Roo.decode(xhr.responseText);
30920 prepare : function(file)
30923 this.maskEl.mask(this.loadingText);
30929 if(typeof(file) === 'string'){
30930 this.loadCanvas(file);
30934 if(!file || !this.urlAPI){
30939 this.cropType = file.type;
30943 if(this.fireEvent('prepare', this, this.file) != false){
30945 var reader = new FileReader();
30947 reader.onload = function (e) {
30948 if (e.target.error) {
30949 Roo.log(e.target.error);
30953 var buffer = e.target.result,
30954 dataView = new DataView(buffer),
30956 maxOffset = dataView.byteLength - 4,
30960 if (dataView.getUint16(0) === 0xffd8) {
30961 while (offset < maxOffset) {
30962 markerBytes = dataView.getUint16(offset);
30964 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30965 markerLength = dataView.getUint16(offset + 2) + 2;
30966 if (offset + markerLength > dataView.byteLength) {
30967 Roo.log('Invalid meta data: Invalid segment size.');
30971 if(markerBytes == 0xffe1){
30972 _this.parseExifData(
30979 offset += markerLength;
30989 var url = _this.urlAPI.createObjectURL(_this.file);
30991 _this.loadCanvas(url);
30996 reader.readAsArrayBuffer(this.file);
31002 parseExifData : function(dataView, offset, length)
31004 var tiffOffset = offset + 10,
31008 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31009 // No Exif data, might be XMP data instead
31013 // Check for the ASCII code for "Exif" (0x45786966):
31014 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31015 // No Exif data, might be XMP data instead
31018 if (tiffOffset + 8 > dataView.byteLength) {
31019 Roo.log('Invalid Exif data: Invalid segment size.');
31022 // Check for the two null bytes:
31023 if (dataView.getUint16(offset + 8) !== 0x0000) {
31024 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31027 // Check the byte alignment:
31028 switch (dataView.getUint16(tiffOffset)) {
31030 littleEndian = true;
31033 littleEndian = false;
31036 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31039 // Check for the TIFF tag marker (0x002A):
31040 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31041 Roo.log('Invalid Exif data: Missing TIFF marker.');
31044 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31045 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31047 this.parseExifTags(
31050 tiffOffset + dirOffset,
31055 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31060 if (dirOffset + 6 > dataView.byteLength) {
31061 Roo.log('Invalid Exif data: Invalid directory offset.');
31064 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31065 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31066 if (dirEndOffset + 4 > dataView.byteLength) {
31067 Roo.log('Invalid Exif data: Invalid directory size.');
31070 for (i = 0; i < tagsNumber; i += 1) {
31074 dirOffset + 2 + 12 * i, // tag offset
31078 // Return the offset to the next directory:
31079 return dataView.getUint32(dirEndOffset, littleEndian);
31082 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31084 var tag = dataView.getUint16(offset, littleEndian);
31086 this.exif[tag] = this.getExifValue(
31090 dataView.getUint16(offset + 2, littleEndian), // tag type
31091 dataView.getUint32(offset + 4, littleEndian), // tag length
31096 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31098 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31107 Roo.log('Invalid Exif data: Invalid tag type.');
31111 tagSize = tagType.size * length;
31112 // Determine if the value is contained in the dataOffset bytes,
31113 // or if the value at the dataOffset is a pointer to the actual data:
31114 dataOffset = tagSize > 4 ?
31115 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31116 if (dataOffset + tagSize > dataView.byteLength) {
31117 Roo.log('Invalid Exif data: Invalid data offset.');
31120 if (length === 1) {
31121 return tagType.getValue(dataView, dataOffset, littleEndian);
31124 for (i = 0; i < length; i += 1) {
31125 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31128 if (tagType.ascii) {
31130 // Concatenate the chars:
31131 for (i = 0; i < values.length; i += 1) {
31133 // Ignore the terminating NULL byte(s):
31134 if (c === '\u0000') {
31146 Roo.apply(Roo.bootstrap.UploadCropbox, {
31148 'Orientation': 0x0112
31152 1: 0, //'top-left',
31154 3: 180, //'bottom-right',
31155 // 4: 'bottom-left',
31157 6: 90, //'right-top',
31158 // 7: 'right-bottom',
31159 8: 270 //'left-bottom'
31163 // byte, 8-bit unsigned int:
31165 getValue: function (dataView, dataOffset) {
31166 return dataView.getUint8(dataOffset);
31170 // ascii, 8-bit byte:
31172 getValue: function (dataView, dataOffset) {
31173 return String.fromCharCode(dataView.getUint8(dataOffset));
31178 // short, 16 bit int:
31180 getValue: function (dataView, dataOffset, littleEndian) {
31181 return dataView.getUint16(dataOffset, littleEndian);
31185 // long, 32 bit int:
31187 getValue: function (dataView, dataOffset, littleEndian) {
31188 return dataView.getUint32(dataOffset, littleEndian);
31192 // rational = two long values, first is numerator, second is denominator:
31194 getValue: function (dataView, dataOffset, littleEndian) {
31195 return dataView.getUint32(dataOffset, littleEndian) /
31196 dataView.getUint32(dataOffset + 4, littleEndian);
31200 // slong, 32 bit signed int:
31202 getValue: function (dataView, dataOffset, littleEndian) {
31203 return dataView.getInt32(dataOffset, littleEndian);
31207 // srational, two slongs, first is numerator, second is denominator:
31209 getValue: function (dataView, dataOffset, littleEndian) {
31210 return dataView.getInt32(dataOffset, littleEndian) /
31211 dataView.getInt32(dataOffset + 4, littleEndian);
31221 cls : 'btn-group roo-upload-cropbox-rotate-left',
31222 action : 'rotate-left',
31226 cls : 'btn btn-default',
31227 html : '<i class="fa fa-undo"></i>'
31233 cls : 'btn-group roo-upload-cropbox-picture',
31234 action : 'picture',
31238 cls : 'btn btn-default',
31239 html : '<i class="fa fa-picture-o"></i>'
31245 cls : 'btn-group roo-upload-cropbox-rotate-right',
31246 action : 'rotate-right',
31250 cls : 'btn btn-default',
31251 html : '<i class="fa fa-repeat"></i>'
31259 cls : 'btn-group roo-upload-cropbox-rotate-left',
31260 action : 'rotate-left',
31264 cls : 'btn btn-default',
31265 html : '<i class="fa fa-undo"></i>'
31271 cls : 'btn-group roo-upload-cropbox-download',
31272 action : 'download',
31276 cls : 'btn btn-default',
31277 html : '<i class="fa fa-download"></i>'
31283 cls : 'btn-group roo-upload-cropbox-crop',
31288 cls : 'btn btn-default',
31289 html : '<i class="fa fa-crop"></i>'
31295 cls : 'btn-group roo-upload-cropbox-trash',
31300 cls : 'btn btn-default',
31301 html : '<i class="fa fa-trash"></i>'
31307 cls : 'btn-group roo-upload-cropbox-rotate-right',
31308 action : 'rotate-right',
31312 cls : 'btn btn-default',
31313 html : '<i class="fa fa-repeat"></i>'
31321 cls : 'btn-group roo-upload-cropbox-rotate-left',
31322 action : 'rotate-left',
31326 cls : 'btn btn-default',
31327 html : '<i class="fa fa-undo"></i>'
31333 cls : 'btn-group roo-upload-cropbox-rotate-right',
31334 action : 'rotate-right',
31338 cls : 'btn btn-default',
31339 html : '<i class="fa fa-repeat"></i>'
31352 * @class Roo.bootstrap.DocumentManager
31353 * @extends Roo.bootstrap.Component
31354 * Bootstrap DocumentManager class
31355 * @cfg {String} paramName default 'imageUpload'
31356 * @cfg {String} toolTipName default 'filename'
31357 * @cfg {String} method default POST
31358 * @cfg {String} url action url
31359 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31360 * @cfg {Boolean} multiple multiple upload default true
31361 * @cfg {Number} thumbSize default 300
31362 * @cfg {String} fieldLabel
31363 * @cfg {Number} labelWidth default 4
31364 * @cfg {String} labelAlign (left|top) default left
31365 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31366 * @cfg {Number} labellg set the width of label (1-12)
31367 * @cfg {Number} labelmd set the width of label (1-12)
31368 * @cfg {Number} labelsm set the width of label (1-12)
31369 * @cfg {Number} labelxs set the width of label (1-12)
31372 * Create a new DocumentManager
31373 * @param {Object} config The config object
31376 Roo.bootstrap.DocumentManager = function(config){
31377 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31380 this.delegates = [];
31385 * Fire when initial the DocumentManager
31386 * @param {Roo.bootstrap.DocumentManager} this
31391 * inspect selected file
31392 * @param {Roo.bootstrap.DocumentManager} this
31393 * @param {File} file
31398 * Fire when xhr load exception
31399 * @param {Roo.bootstrap.DocumentManager} this
31400 * @param {XMLHttpRequest} xhr
31402 "exception" : true,
31404 * @event afterupload
31405 * Fire when xhr load exception
31406 * @param {Roo.bootstrap.DocumentManager} this
31407 * @param {XMLHttpRequest} xhr
31409 "afterupload" : true,
31412 * prepare the form data
31413 * @param {Roo.bootstrap.DocumentManager} this
31414 * @param {Object} formData
31419 * Fire when remove the file
31420 * @param {Roo.bootstrap.DocumentManager} this
31421 * @param {Object} file
31426 * Fire after refresh the file
31427 * @param {Roo.bootstrap.DocumentManager} this
31432 * Fire after click the image
31433 * @param {Roo.bootstrap.DocumentManager} this
31434 * @param {Object} file
31439 * Fire when upload a image and editable set to true
31440 * @param {Roo.bootstrap.DocumentManager} this
31441 * @param {Object} file
31445 * @event beforeselectfile
31446 * Fire before select file
31447 * @param {Roo.bootstrap.DocumentManager} this
31449 "beforeselectfile" : true,
31452 * Fire before process file
31453 * @param {Roo.bootstrap.DocumentManager} this
31454 * @param {Object} file
31458 * @event previewrendered
31459 * Fire when preview rendered
31460 * @param {Roo.bootstrap.DocumentManager} this
31461 * @param {Object} file
31463 "previewrendered" : true,
31466 "previewResize" : true
31471 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31480 paramName : 'imageUpload',
31481 toolTipName : 'filename',
31484 labelAlign : 'left',
31494 getAutoCreate : function()
31496 var managerWidget = {
31498 cls : 'roo-document-manager',
31502 cls : 'roo-document-manager-selector',
31507 cls : 'roo-document-manager-uploader',
31511 cls : 'roo-document-manager-upload-btn',
31512 html : '<i class="fa fa-plus"></i>'
31523 cls : 'column col-md-12',
31528 if(this.fieldLabel.length){
31533 cls : 'column col-md-12',
31534 html : this.fieldLabel
31538 cls : 'column col-md-12',
31543 if(this.labelAlign == 'left'){
31548 html : this.fieldLabel
31557 if(this.labelWidth > 12){
31558 content[0].style = "width: " + this.labelWidth + 'px';
31561 if(this.labelWidth < 13 && this.labelmd == 0){
31562 this.labelmd = this.labelWidth;
31565 if(this.labellg > 0){
31566 content[0].cls += ' col-lg-' + this.labellg;
31567 content[1].cls += ' col-lg-' + (12 - this.labellg);
31570 if(this.labelmd > 0){
31571 content[0].cls += ' col-md-' + this.labelmd;
31572 content[1].cls += ' col-md-' + (12 - this.labelmd);
31575 if(this.labelsm > 0){
31576 content[0].cls += ' col-sm-' + this.labelsm;
31577 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31580 if(this.labelxs > 0){
31581 content[0].cls += ' col-xs-' + this.labelxs;
31582 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31590 cls : 'row clearfix',
31598 initEvents : function()
31600 this.managerEl = this.el.select('.roo-document-manager', true).first();
31601 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31603 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31604 this.selectorEl.hide();
31607 this.selectorEl.attr('multiple', 'multiple');
31610 this.selectorEl.on('change', this.onFileSelected, this);
31612 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31613 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31615 this.uploader.on('click', this.onUploaderClick, this);
31617 this.renderProgressDialog();
31621 window.addEventListener("resize", function() { _this.refresh(); } );
31623 this.fireEvent('initial', this);
31626 renderProgressDialog : function()
31630 this.progressDialog = new Roo.bootstrap.Modal({
31631 cls : 'roo-document-manager-progress-dialog',
31632 allow_close : false,
31643 btnclick : function() {
31644 _this.uploadCancel();
31650 this.progressDialog.render(Roo.get(document.body));
31652 this.progress = new Roo.bootstrap.Progress({
31653 cls : 'roo-document-manager-progress',
31658 this.progress.render(this.progressDialog.getChildContainer());
31660 this.progressBar = new Roo.bootstrap.ProgressBar({
31661 cls : 'roo-document-manager-progress-bar',
31664 aria_valuemax : 12,
31668 this.progressBar.render(this.progress.getChildContainer());
31671 onUploaderClick : function(e)
31673 e.preventDefault();
31675 if(this.fireEvent('beforeselectfile', this) != false){
31676 this.selectorEl.dom.click();
31681 onFileSelected : function(e)
31683 e.preventDefault();
31685 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31689 Roo.each(this.selectorEl.dom.files, function(file){
31690 if(this.fireEvent('inspect', this, file) != false){
31691 this.files.push(file);
31701 this.selectorEl.dom.value = '';
31703 if(!this.files || !this.files.length){
31707 if(this.boxes > 0 && this.files.length > this.boxes){
31708 this.files = this.files.slice(0, this.boxes);
31711 this.uploader.show();
31713 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31714 this.uploader.hide();
31723 Roo.each(this.files, function(file){
31725 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31726 var f = this.renderPreview(file);
31731 if(file.type.indexOf('image') != -1){
31732 this.delegates.push(
31734 _this.process(file);
31735 }).createDelegate(this)
31743 _this.process(file);
31744 }).createDelegate(this)
31749 this.files = files;
31751 this.delegates = this.delegates.concat(docs);
31753 if(!this.delegates.length){
31758 this.progressBar.aria_valuemax = this.delegates.length;
31765 arrange : function()
31767 if(!this.delegates.length){
31768 this.progressDialog.hide();
31773 var delegate = this.delegates.shift();
31775 this.progressDialog.show();
31777 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31779 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31784 refresh : function()
31786 this.uploader.show();
31788 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31789 this.uploader.hide();
31792 Roo.isTouch ? this.closable(false) : this.closable(true);
31794 this.fireEvent('refresh', this);
31797 onRemove : function(e, el, o)
31799 e.preventDefault();
31801 this.fireEvent('remove', this, o);
31805 remove : function(o)
31809 Roo.each(this.files, function(file){
31810 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31819 this.files = files;
31826 Roo.each(this.files, function(file){
31831 file.target.remove();
31840 onClick : function(e, el, o)
31842 e.preventDefault();
31844 this.fireEvent('click', this, o);
31848 closable : function(closable)
31850 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31852 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31864 xhrOnLoad : function(xhr)
31866 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31870 if (xhr.readyState !== 4) {
31872 this.fireEvent('exception', this, xhr);
31876 var response = Roo.decode(xhr.responseText);
31878 if(!response.success){
31880 this.fireEvent('exception', this, xhr);
31884 var file = this.renderPreview(response.data);
31886 this.files.push(file);
31890 this.fireEvent('afterupload', this, xhr);
31894 xhrOnError : function(xhr)
31896 Roo.log('xhr on error');
31898 var response = Roo.decode(xhr.responseText);
31905 process : function(file)
31907 if(this.fireEvent('process', this, file) !== false){
31908 if(this.editable && file.type.indexOf('image') != -1){
31909 this.fireEvent('edit', this, file);
31913 this.uploadStart(file, false);
31920 uploadStart : function(file, crop)
31922 this.xhr = new XMLHttpRequest();
31924 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31929 file.xhr = this.xhr;
31931 this.managerEl.createChild({
31933 cls : 'roo-document-manager-loading',
31937 tooltip : file.name,
31938 cls : 'roo-document-manager-thumb',
31939 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31945 this.xhr.open(this.method, this.url, true);
31948 "Accept": "application/json",
31949 "Cache-Control": "no-cache",
31950 "X-Requested-With": "XMLHttpRequest"
31953 for (var headerName in headers) {
31954 var headerValue = headers[headerName];
31956 this.xhr.setRequestHeader(headerName, headerValue);
31962 this.xhr.onload = function()
31964 _this.xhrOnLoad(_this.xhr);
31967 this.xhr.onerror = function()
31969 _this.xhrOnError(_this.xhr);
31972 var formData = new FormData();
31974 formData.append('returnHTML', 'NO');
31977 formData.append('crop', crop);
31980 formData.append(this.paramName, file, file.name);
31987 if(this.fireEvent('prepare', this, formData, options) != false){
31989 if(options.manually){
31993 this.xhr.send(formData);
31997 this.uploadCancel();
32000 uploadCancel : function()
32006 this.delegates = [];
32008 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32015 renderPreview : function(file)
32017 if(typeof(file.target) != 'undefined' && file.target){
32021 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32023 var previewEl = this.managerEl.createChild({
32025 cls : 'roo-document-manager-preview',
32029 tooltip : file[this.toolTipName],
32030 cls : 'roo-document-manager-thumb',
32031 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32036 html : '<i class="fa fa-times-circle"></i>'
32041 var close = previewEl.select('button.close', true).first();
32043 close.on('click', this.onRemove, this, file);
32045 file.target = previewEl;
32047 var image = previewEl.select('img', true).first();
32051 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32053 image.on('click', this.onClick, this, file);
32055 this.fireEvent('previewrendered', this, file);
32061 onPreviewLoad : function(file, image)
32063 if(typeof(file.target) == 'undefined' || !file.target){
32067 var width = image.dom.naturalWidth || image.dom.width;
32068 var height = image.dom.naturalHeight || image.dom.height;
32070 if(!this.previewResize) {
32074 if(width > height){
32075 file.target.addClass('wide');
32079 file.target.addClass('tall');
32084 uploadFromSource : function(file, crop)
32086 this.xhr = new XMLHttpRequest();
32088 this.managerEl.createChild({
32090 cls : 'roo-document-manager-loading',
32094 tooltip : file.name,
32095 cls : 'roo-document-manager-thumb',
32096 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32102 this.xhr.open(this.method, this.url, true);
32105 "Accept": "application/json",
32106 "Cache-Control": "no-cache",
32107 "X-Requested-With": "XMLHttpRequest"
32110 for (var headerName in headers) {
32111 var headerValue = headers[headerName];
32113 this.xhr.setRequestHeader(headerName, headerValue);
32119 this.xhr.onload = function()
32121 _this.xhrOnLoad(_this.xhr);
32124 this.xhr.onerror = function()
32126 _this.xhrOnError(_this.xhr);
32129 var formData = new FormData();
32131 formData.append('returnHTML', 'NO');
32133 formData.append('crop', crop);
32135 if(typeof(file.filename) != 'undefined'){
32136 formData.append('filename', file.filename);
32139 if(typeof(file.mimetype) != 'undefined'){
32140 formData.append('mimetype', file.mimetype);
32145 if(this.fireEvent('prepare', this, formData) != false){
32146 this.xhr.send(formData);
32156 * @class Roo.bootstrap.DocumentViewer
32157 * @extends Roo.bootstrap.Component
32158 * Bootstrap DocumentViewer class
32159 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32160 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32163 * Create a new DocumentViewer
32164 * @param {Object} config The config object
32167 Roo.bootstrap.DocumentViewer = function(config){
32168 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32173 * Fire after initEvent
32174 * @param {Roo.bootstrap.DocumentViewer} this
32180 * @param {Roo.bootstrap.DocumentViewer} this
32185 * Fire after download button
32186 * @param {Roo.bootstrap.DocumentViewer} this
32191 * Fire after trash button
32192 * @param {Roo.bootstrap.DocumentViewer} this
32199 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32201 showDownload : true,
32205 getAutoCreate : function()
32209 cls : 'roo-document-viewer',
32213 cls : 'roo-document-viewer-body',
32217 cls : 'roo-document-viewer-thumb',
32221 cls : 'roo-document-viewer-image'
32229 cls : 'roo-document-viewer-footer',
32232 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32236 cls : 'btn-group roo-document-viewer-download',
32240 cls : 'btn btn-default',
32241 html : '<i class="fa fa-download"></i>'
32247 cls : 'btn-group roo-document-viewer-trash',
32251 cls : 'btn btn-default',
32252 html : '<i class="fa fa-trash"></i>'
32265 initEvents : function()
32267 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32268 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32270 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32271 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32273 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32274 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32276 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32277 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32279 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32280 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32282 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32283 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32285 this.bodyEl.on('click', this.onClick, this);
32286 this.downloadBtn.on('click', this.onDownload, this);
32287 this.trashBtn.on('click', this.onTrash, this);
32289 this.downloadBtn.hide();
32290 this.trashBtn.hide();
32292 if(this.showDownload){
32293 this.downloadBtn.show();
32296 if(this.showTrash){
32297 this.trashBtn.show();
32300 if(!this.showDownload && !this.showTrash) {
32301 this.footerEl.hide();
32306 initial : function()
32308 this.fireEvent('initial', this);
32312 onClick : function(e)
32314 e.preventDefault();
32316 this.fireEvent('click', this);
32319 onDownload : function(e)
32321 e.preventDefault();
32323 this.fireEvent('download', this);
32326 onTrash : function(e)
32328 e.preventDefault();
32330 this.fireEvent('trash', this);
32342 * @class Roo.bootstrap.NavProgressBar
32343 * @extends Roo.bootstrap.Component
32344 * Bootstrap NavProgressBar class
32347 * Create a new nav progress bar
32348 * @param {Object} config The config object
32351 Roo.bootstrap.NavProgressBar = function(config){
32352 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32354 this.bullets = this.bullets || [];
32356 // Roo.bootstrap.NavProgressBar.register(this);
32360 * Fires when the active item changes
32361 * @param {Roo.bootstrap.NavProgressBar} this
32362 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32363 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32370 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32375 getAutoCreate : function()
32377 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32381 cls : 'roo-navigation-bar-group',
32385 cls : 'roo-navigation-top-bar'
32389 cls : 'roo-navigation-bullets-bar',
32393 cls : 'roo-navigation-bar'
32400 cls : 'roo-navigation-bottom-bar'
32410 initEvents: function()
32415 onRender : function(ct, position)
32417 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32419 if(this.bullets.length){
32420 Roo.each(this.bullets, function(b){
32429 addItem : function(cfg)
32431 var item = new Roo.bootstrap.NavProgressItem(cfg);
32433 item.parentId = this.id;
32434 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32437 var top = new Roo.bootstrap.Element({
32439 cls : 'roo-navigation-bar-text'
32442 var bottom = new Roo.bootstrap.Element({
32444 cls : 'roo-navigation-bar-text'
32447 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32448 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32450 var topText = new Roo.bootstrap.Element({
32452 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32455 var bottomText = new Roo.bootstrap.Element({
32457 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32460 topText.onRender(top.el, null);
32461 bottomText.onRender(bottom.el, null);
32464 item.bottomEl = bottom;
32467 this.barItems.push(item);
32472 getActive : function()
32474 var active = false;
32476 Roo.each(this.barItems, function(v){
32478 if (!v.isActive()) {
32490 setActiveItem : function(item)
32494 Roo.each(this.barItems, function(v){
32495 if (v.rid == item.rid) {
32499 if (v.isActive()) {
32500 v.setActive(false);
32505 item.setActive(true);
32507 this.fireEvent('changed', this, item, prev);
32510 getBarItem: function(rid)
32514 Roo.each(this.barItems, function(e) {
32515 if (e.rid != rid) {
32526 indexOfItem : function(item)
32530 Roo.each(this.barItems, function(v, i){
32532 if (v.rid != item.rid) {
32543 setActiveNext : function()
32545 var i = this.indexOfItem(this.getActive());
32547 if (i > this.barItems.length) {
32551 this.setActiveItem(this.barItems[i+1]);
32554 setActivePrev : function()
32556 var i = this.indexOfItem(this.getActive());
32562 this.setActiveItem(this.barItems[i-1]);
32565 format : function()
32567 if(!this.barItems.length){
32571 var width = 100 / this.barItems.length;
32573 Roo.each(this.barItems, function(i){
32574 i.el.setStyle('width', width + '%');
32575 i.topEl.el.setStyle('width', width + '%');
32576 i.bottomEl.el.setStyle('width', width + '%');
32585 * Nav Progress Item
32590 * @class Roo.bootstrap.NavProgressItem
32591 * @extends Roo.bootstrap.Component
32592 * Bootstrap NavProgressItem class
32593 * @cfg {String} rid the reference id
32594 * @cfg {Boolean} active (true|false) Is item active default false
32595 * @cfg {Boolean} disabled (true|false) Is item active default false
32596 * @cfg {String} html
32597 * @cfg {String} position (top|bottom) text position default bottom
32598 * @cfg {String} icon show icon instead of number
32601 * Create a new NavProgressItem
32602 * @param {Object} config The config object
32604 Roo.bootstrap.NavProgressItem = function(config){
32605 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32610 * The raw click event for the entire grid.
32611 * @param {Roo.bootstrap.NavProgressItem} this
32612 * @param {Roo.EventObject} e
32619 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32625 position : 'bottom',
32628 getAutoCreate : function()
32630 var iconCls = 'roo-navigation-bar-item-icon';
32632 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32636 cls: 'roo-navigation-bar-item',
32646 cfg.cls += ' active';
32649 cfg.cls += ' disabled';
32655 disable : function()
32657 this.setDisabled(true);
32660 enable : function()
32662 this.setDisabled(false);
32665 initEvents: function()
32667 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32669 this.iconEl.on('click', this.onClick, this);
32672 onClick : function(e)
32674 e.preventDefault();
32680 if(this.fireEvent('click', this, e) === false){
32684 this.parent().setActiveItem(this);
32687 isActive: function ()
32689 return this.active;
32692 setActive : function(state)
32694 if(this.active == state){
32698 this.active = state;
32701 this.el.addClass('active');
32705 this.el.removeClass('active');
32710 setDisabled : function(state)
32712 if(this.disabled == state){
32716 this.disabled = state;
32719 this.el.addClass('disabled');
32723 this.el.removeClass('disabled');
32726 tooltipEl : function()
32728 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32741 * @class Roo.bootstrap.FieldLabel
32742 * @extends Roo.bootstrap.Component
32743 * Bootstrap FieldLabel class
32744 * @cfg {String} html contents of the element
32745 * @cfg {String} tag tag of the element default label
32746 * @cfg {String} cls class of the element
32747 * @cfg {String} target label target
32748 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32749 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32750 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32751 * @cfg {String} iconTooltip default "This field is required"
32752 * @cfg {String} indicatorpos (left|right) default left
32755 * Create a new FieldLabel
32756 * @param {Object} config The config object
32759 Roo.bootstrap.FieldLabel = function(config){
32760 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32765 * Fires after the field has been marked as invalid.
32766 * @param {Roo.form.FieldLabel} this
32767 * @param {String} msg The validation message
32772 * Fires after the field has been validated with no errors.
32773 * @param {Roo.form.FieldLabel} this
32779 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32786 invalidClass : 'has-warning',
32787 validClass : 'has-success',
32788 iconTooltip : 'This field is required',
32789 indicatorpos : 'left',
32791 getAutoCreate : function(){
32794 if (!this.allowBlank) {
32800 cls : 'roo-bootstrap-field-label ' + this.cls,
32805 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32806 tooltip : this.iconTooltip
32815 if(this.indicatorpos == 'right'){
32818 cls : 'roo-bootstrap-field-label ' + this.cls,
32827 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32828 tooltip : this.iconTooltip
32837 initEvents: function()
32839 Roo.bootstrap.Element.superclass.initEvents.call(this);
32841 this.indicator = this.indicatorEl();
32843 if(this.indicator){
32844 this.indicator.removeClass('visible');
32845 this.indicator.addClass('invisible');
32848 Roo.bootstrap.FieldLabel.register(this);
32851 indicatorEl : function()
32853 var indicator = this.el.select('i.roo-required-indicator',true).first();
32864 * Mark this field as valid
32866 markValid : function()
32868 if(this.indicator){
32869 this.indicator.removeClass('visible');
32870 this.indicator.addClass('invisible');
32872 if (Roo.bootstrap.version == 3) {
32873 this.el.removeClass(this.invalidClass);
32874 this.el.addClass(this.validClass);
32876 this.el.removeClass('is-invalid');
32877 this.el.addClass('is-valid');
32881 this.fireEvent('valid', this);
32885 * Mark this field as invalid
32886 * @param {String} msg The validation message
32888 markInvalid : function(msg)
32890 if(this.indicator){
32891 this.indicator.removeClass('invisible');
32892 this.indicator.addClass('visible');
32894 if (Roo.bootstrap.version == 3) {
32895 this.el.removeClass(this.validClass);
32896 this.el.addClass(this.invalidClass);
32898 this.el.removeClass('is-valid');
32899 this.el.addClass('is-invalid');
32903 this.fireEvent('invalid', this, msg);
32909 Roo.apply(Roo.bootstrap.FieldLabel, {
32914 * register a FieldLabel Group
32915 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32917 register : function(label)
32919 if(this.groups.hasOwnProperty(label.target)){
32923 this.groups[label.target] = label;
32927 * fetch a FieldLabel Group based on the target
32928 * @param {string} target
32929 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32931 get: function(target) {
32932 if (typeof(this.groups[target]) == 'undefined') {
32936 return this.groups[target] ;
32945 * page DateSplitField.
32951 * @class Roo.bootstrap.DateSplitField
32952 * @extends Roo.bootstrap.Component
32953 * Bootstrap DateSplitField class
32954 * @cfg {string} fieldLabel - the label associated
32955 * @cfg {Number} labelWidth set the width of label (0-12)
32956 * @cfg {String} labelAlign (top|left)
32957 * @cfg {Boolean} dayAllowBlank (true|false) default false
32958 * @cfg {Boolean} monthAllowBlank (true|false) default false
32959 * @cfg {Boolean} yearAllowBlank (true|false) default false
32960 * @cfg {string} dayPlaceholder
32961 * @cfg {string} monthPlaceholder
32962 * @cfg {string} yearPlaceholder
32963 * @cfg {string} dayFormat default 'd'
32964 * @cfg {string} monthFormat default 'm'
32965 * @cfg {string} yearFormat default 'Y'
32966 * @cfg {Number} labellg set the width of label (1-12)
32967 * @cfg {Number} labelmd set the width of label (1-12)
32968 * @cfg {Number} labelsm set the width of label (1-12)
32969 * @cfg {Number} labelxs set the width of label (1-12)
32973 * Create a new DateSplitField
32974 * @param {Object} config The config object
32977 Roo.bootstrap.DateSplitField = function(config){
32978 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32984 * getting the data of years
32985 * @param {Roo.bootstrap.DateSplitField} this
32986 * @param {Object} years
32991 * getting the data of days
32992 * @param {Roo.bootstrap.DateSplitField} this
32993 * @param {Object} days
32998 * Fires after the field has been marked as invalid.
32999 * @param {Roo.form.Field} this
33000 * @param {String} msg The validation message
33005 * Fires after the field has been validated with no errors.
33006 * @param {Roo.form.Field} this
33012 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33015 labelAlign : 'top',
33017 dayAllowBlank : false,
33018 monthAllowBlank : false,
33019 yearAllowBlank : false,
33020 dayPlaceholder : '',
33021 monthPlaceholder : '',
33022 yearPlaceholder : '',
33026 isFormField : true,
33032 getAutoCreate : function()
33036 cls : 'row roo-date-split-field-group',
33041 cls : 'form-hidden-field roo-date-split-field-group-value',
33047 var labelCls = 'col-md-12';
33048 var contentCls = 'col-md-4';
33050 if(this.fieldLabel){
33054 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33058 html : this.fieldLabel
33063 if(this.labelAlign == 'left'){
33065 if(this.labelWidth > 12){
33066 label.style = "width: " + this.labelWidth + 'px';
33069 if(this.labelWidth < 13 && this.labelmd == 0){
33070 this.labelmd = this.labelWidth;
33073 if(this.labellg > 0){
33074 labelCls = ' col-lg-' + this.labellg;
33075 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33078 if(this.labelmd > 0){
33079 labelCls = ' col-md-' + this.labelmd;
33080 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33083 if(this.labelsm > 0){
33084 labelCls = ' col-sm-' + this.labelsm;
33085 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33088 if(this.labelxs > 0){
33089 labelCls = ' col-xs-' + this.labelxs;
33090 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33094 label.cls += ' ' + labelCls;
33096 cfg.cn.push(label);
33099 Roo.each(['day', 'month', 'year'], function(t){
33102 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33109 inputEl: function ()
33111 return this.el.select('.roo-date-split-field-group-value', true).first();
33114 onRender : function(ct, position)
33118 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33120 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33122 this.dayField = new Roo.bootstrap.ComboBox({
33123 allowBlank : this.dayAllowBlank,
33124 alwaysQuery : true,
33125 displayField : 'value',
33128 forceSelection : true,
33130 placeholder : this.dayPlaceholder,
33131 selectOnFocus : true,
33132 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33133 triggerAction : 'all',
33135 valueField : 'value',
33136 store : new Roo.data.SimpleStore({
33137 data : (function() {
33139 _this.fireEvent('days', _this, days);
33142 fields : [ 'value' ]
33145 select : function (_self, record, index)
33147 _this.setValue(_this.getValue());
33152 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33154 this.monthField = new Roo.bootstrap.MonthField({
33155 after : '<i class=\"fa fa-calendar\"></i>',
33156 allowBlank : this.monthAllowBlank,
33157 placeholder : this.monthPlaceholder,
33160 render : function (_self)
33162 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33163 e.preventDefault();
33167 select : function (_self, oldvalue, newvalue)
33169 _this.setValue(_this.getValue());
33174 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33176 this.yearField = new Roo.bootstrap.ComboBox({
33177 allowBlank : this.yearAllowBlank,
33178 alwaysQuery : true,
33179 displayField : 'value',
33182 forceSelection : true,
33184 placeholder : this.yearPlaceholder,
33185 selectOnFocus : true,
33186 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33187 triggerAction : 'all',
33189 valueField : 'value',
33190 store : new Roo.data.SimpleStore({
33191 data : (function() {
33193 _this.fireEvent('years', _this, years);
33196 fields : [ 'value' ]
33199 select : function (_self, record, index)
33201 _this.setValue(_this.getValue());
33206 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33209 setValue : function(v, format)
33211 this.inputEl.dom.value = v;
33213 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33215 var d = Date.parseDate(v, f);
33222 this.setDay(d.format(this.dayFormat));
33223 this.setMonth(d.format(this.monthFormat));
33224 this.setYear(d.format(this.yearFormat));
33231 setDay : function(v)
33233 this.dayField.setValue(v);
33234 this.inputEl.dom.value = this.getValue();
33239 setMonth : function(v)
33241 this.monthField.setValue(v, true);
33242 this.inputEl.dom.value = this.getValue();
33247 setYear : function(v)
33249 this.yearField.setValue(v);
33250 this.inputEl.dom.value = this.getValue();
33255 getDay : function()
33257 return this.dayField.getValue();
33260 getMonth : function()
33262 return this.monthField.getValue();
33265 getYear : function()
33267 return this.yearField.getValue();
33270 getValue : function()
33272 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33274 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33284 this.inputEl.dom.value = '';
33289 validate : function()
33291 var d = this.dayField.validate();
33292 var m = this.monthField.validate();
33293 var y = this.yearField.validate();
33298 (!this.dayAllowBlank && !d) ||
33299 (!this.monthAllowBlank && !m) ||
33300 (!this.yearAllowBlank && !y)
33305 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33314 this.markInvalid();
33319 markValid : function()
33322 var label = this.el.select('label', true).first();
33323 var icon = this.el.select('i.fa-star', true).first();
33329 this.fireEvent('valid', this);
33333 * Mark this field as invalid
33334 * @param {String} msg The validation message
33336 markInvalid : function(msg)
33339 var label = this.el.select('label', true).first();
33340 var icon = this.el.select('i.fa-star', true).first();
33342 if(label && !icon){
33343 this.el.select('.roo-date-split-field-label', true).createChild({
33345 cls : 'text-danger fa fa-lg fa-star',
33346 tooltip : 'This field is required',
33347 style : 'margin-right:5px;'
33351 this.fireEvent('invalid', this, msg);
33354 clearInvalid : function()
33356 var label = this.el.select('label', true).first();
33357 var icon = this.el.select('i.fa-star', true).first();
33363 this.fireEvent('valid', this);
33366 getName: function()
33376 * http://masonry.desandro.com
33378 * The idea is to render all the bricks based on vertical width...
33380 * The original code extends 'outlayer' - we might need to use that....
33386 * @class Roo.bootstrap.LayoutMasonry
33387 * @extends Roo.bootstrap.Component
33388 * Bootstrap Layout Masonry class
33391 * Create a new Element
33392 * @param {Object} config The config object
33395 Roo.bootstrap.LayoutMasonry = function(config){
33397 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33401 Roo.bootstrap.LayoutMasonry.register(this);
33407 * Fire after layout the items
33408 * @param {Roo.bootstrap.LayoutMasonry} this
33409 * @param {Roo.EventObject} e
33416 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33419 * @cfg {Boolean} isLayoutInstant = no animation?
33421 isLayoutInstant : false, // needed?
33424 * @cfg {Number} boxWidth width of the columns
33429 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33434 * @cfg {Number} padWidth padding below box..
33439 * @cfg {Number} gutter gutter width..
33444 * @cfg {Number} maxCols maximum number of columns
33450 * @cfg {Boolean} isAutoInitial defalut true
33452 isAutoInitial : true,
33457 * @cfg {Boolean} isHorizontal defalut false
33459 isHorizontal : false,
33461 currentSize : null,
33467 bricks: null, //CompositeElement
33471 _isLayoutInited : false,
33473 // isAlternative : false, // only use for vertical layout...
33476 * @cfg {Number} alternativePadWidth padding below box..
33478 alternativePadWidth : 50,
33480 selectedBrick : [],
33482 getAutoCreate : function(){
33484 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33488 cls: 'blog-masonary-wrapper ' + this.cls,
33490 cls : 'mas-boxes masonary'
33497 getChildContainer: function( )
33499 if (this.boxesEl) {
33500 return this.boxesEl;
33503 this.boxesEl = this.el.select('.mas-boxes').first();
33505 return this.boxesEl;
33509 initEvents : function()
33513 if(this.isAutoInitial){
33514 Roo.log('hook children rendered');
33515 this.on('childrenrendered', function() {
33516 Roo.log('children rendered');
33522 initial : function()
33524 this.selectedBrick = [];
33526 this.currentSize = this.el.getBox(true);
33528 Roo.EventManager.onWindowResize(this.resize, this);
33530 if(!this.isAutoInitial){
33538 //this.layout.defer(500,this);
33542 resize : function()
33544 var cs = this.el.getBox(true);
33547 this.currentSize.width == cs.width &&
33548 this.currentSize.x == cs.x &&
33549 this.currentSize.height == cs.height &&
33550 this.currentSize.y == cs.y
33552 Roo.log("no change in with or X or Y");
33556 this.currentSize = cs;
33562 layout : function()
33564 this._resetLayout();
33566 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33568 this.layoutItems( isInstant );
33570 this._isLayoutInited = true;
33572 this.fireEvent('layout', this);
33576 _resetLayout : function()
33578 if(this.isHorizontal){
33579 this.horizontalMeasureColumns();
33583 this.verticalMeasureColumns();
33587 verticalMeasureColumns : function()
33589 this.getContainerWidth();
33591 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33592 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33596 var boxWidth = this.boxWidth + this.padWidth;
33598 if(this.containerWidth < this.boxWidth){
33599 boxWidth = this.containerWidth
33602 var containerWidth = this.containerWidth;
33604 var cols = Math.floor(containerWidth / boxWidth);
33606 this.cols = Math.max( cols, 1 );
33608 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33610 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33612 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33614 this.colWidth = boxWidth + avail - this.padWidth;
33616 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33617 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33620 horizontalMeasureColumns : function()
33622 this.getContainerWidth();
33624 var boxWidth = this.boxWidth;
33626 if(this.containerWidth < boxWidth){
33627 boxWidth = this.containerWidth;
33630 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33632 this.el.setHeight(boxWidth);
33636 getContainerWidth : function()
33638 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33641 layoutItems : function( isInstant )
33643 Roo.log(this.bricks);
33645 var items = Roo.apply([], this.bricks);
33647 if(this.isHorizontal){
33648 this._horizontalLayoutItems( items , isInstant );
33652 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33653 // this._verticalAlternativeLayoutItems( items , isInstant );
33657 this._verticalLayoutItems( items , isInstant );
33661 _verticalLayoutItems : function ( items , isInstant)
33663 if ( !items || !items.length ) {
33668 ['xs', 'xs', 'xs', 'tall'],
33669 ['xs', 'xs', 'tall'],
33670 ['xs', 'xs', 'sm'],
33671 ['xs', 'xs', 'xs'],
33677 ['sm', 'xs', 'xs'],
33681 ['tall', 'xs', 'xs', 'xs'],
33682 ['tall', 'xs', 'xs'],
33694 Roo.each(items, function(item, k){
33696 switch (item.size) {
33697 // these layouts take up a full box,
33708 boxes.push([item]);
33731 var filterPattern = function(box, length)
33739 var pattern = box.slice(0, length);
33743 Roo.each(pattern, function(i){
33744 format.push(i.size);
33747 Roo.each(standard, function(s){
33749 if(String(s) != String(format)){
33758 if(!match && length == 1){
33763 filterPattern(box, length - 1);
33767 queue.push(pattern);
33769 box = box.slice(length, box.length);
33771 filterPattern(box, 4);
33777 Roo.each(boxes, function(box, k){
33783 if(box.length == 1){
33788 filterPattern(box, 4);
33792 this._processVerticalLayoutQueue( queue, isInstant );
33796 // _verticalAlternativeLayoutItems : function( items , isInstant )
33798 // if ( !items || !items.length ) {
33802 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33806 _horizontalLayoutItems : function ( items , isInstant)
33808 if ( !items || !items.length || items.length < 3) {
33814 var eItems = items.slice(0, 3);
33816 items = items.slice(3, items.length);
33819 ['xs', 'xs', 'xs', 'wide'],
33820 ['xs', 'xs', 'wide'],
33821 ['xs', 'xs', 'sm'],
33822 ['xs', 'xs', 'xs'],
33828 ['sm', 'xs', 'xs'],
33832 ['wide', 'xs', 'xs', 'xs'],
33833 ['wide', 'xs', 'xs'],
33846 Roo.each(items, function(item, k){
33848 switch (item.size) {
33859 boxes.push([item]);
33883 var filterPattern = function(box, length)
33891 var pattern = box.slice(0, length);
33895 Roo.each(pattern, function(i){
33896 format.push(i.size);
33899 Roo.each(standard, function(s){
33901 if(String(s) != String(format)){
33910 if(!match && length == 1){
33915 filterPattern(box, length - 1);
33919 queue.push(pattern);
33921 box = box.slice(length, box.length);
33923 filterPattern(box, 4);
33929 Roo.each(boxes, function(box, k){
33935 if(box.length == 1){
33940 filterPattern(box, 4);
33947 var pos = this.el.getBox(true);
33951 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33953 var hit_end = false;
33955 Roo.each(queue, function(box){
33959 Roo.each(box, function(b){
33961 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33971 Roo.each(box, function(b){
33973 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33976 mx = Math.max(mx, b.x);
33980 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33984 Roo.each(box, function(b){
33986 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34000 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34003 /** Sets position of item in DOM
34004 * @param {Element} item
34005 * @param {Number} x - horizontal position
34006 * @param {Number} y - vertical position
34007 * @param {Boolean} isInstant - disables transitions
34009 _processVerticalLayoutQueue : function( queue, isInstant )
34011 var pos = this.el.getBox(true);
34016 for (var i = 0; i < this.cols; i++){
34020 Roo.each(queue, function(box, k){
34022 var col = k % this.cols;
34024 Roo.each(box, function(b,kk){
34026 b.el.position('absolute');
34028 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34029 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34031 if(b.size == 'md-left' || b.size == 'md-right'){
34032 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34033 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34036 b.el.setWidth(width);
34037 b.el.setHeight(height);
34039 b.el.select('iframe',true).setSize(width,height);
34043 for (var i = 0; i < this.cols; i++){
34045 if(maxY[i] < maxY[col]){
34050 col = Math.min(col, i);
34054 x = pos.x + col * (this.colWidth + this.padWidth);
34058 var positions = [];
34060 switch (box.length){
34062 positions = this.getVerticalOneBoxColPositions(x, y, box);
34065 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34068 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34071 positions = this.getVerticalFourBoxColPositions(x, y, box);
34077 Roo.each(box, function(b,kk){
34079 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34081 var sz = b.el.getSize();
34083 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34091 for (var i = 0; i < this.cols; i++){
34092 mY = Math.max(mY, maxY[i]);
34095 this.el.setHeight(mY - pos.y);
34099 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34101 // var pos = this.el.getBox(true);
34104 // var maxX = pos.right;
34106 // var maxHeight = 0;
34108 // Roo.each(items, function(item, k){
34112 // item.el.position('absolute');
34114 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34116 // item.el.setWidth(width);
34118 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34120 // item.el.setHeight(height);
34123 // item.el.setXY([x, y], isInstant ? false : true);
34125 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34128 // y = y + height + this.alternativePadWidth;
34130 // maxHeight = maxHeight + height + this.alternativePadWidth;
34134 // this.el.setHeight(maxHeight);
34138 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34140 var pos = this.el.getBox(true);
34145 var maxX = pos.right;
34147 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34149 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34151 Roo.each(queue, function(box, k){
34153 Roo.each(box, function(b, kk){
34155 b.el.position('absolute');
34157 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34158 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34160 if(b.size == 'md-left' || b.size == 'md-right'){
34161 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34162 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34165 b.el.setWidth(width);
34166 b.el.setHeight(height);
34174 var positions = [];
34176 switch (box.length){
34178 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34181 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34184 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34187 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34193 Roo.each(box, function(b,kk){
34195 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34197 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34205 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34207 Roo.each(eItems, function(b,k){
34209 b.size = (k == 0) ? 'sm' : 'xs';
34210 b.x = (k == 0) ? 2 : 1;
34211 b.y = (k == 0) ? 2 : 1;
34213 b.el.position('absolute');
34215 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34217 b.el.setWidth(width);
34219 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34221 b.el.setHeight(height);
34225 var positions = [];
34228 x : maxX - this.unitWidth * 2 - this.gutter,
34233 x : maxX - this.unitWidth,
34234 y : minY + (this.unitWidth + this.gutter) * 2
34238 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34242 Roo.each(eItems, function(b,k){
34244 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34250 getVerticalOneBoxColPositions : function(x, y, box)
34254 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34256 if(box[0].size == 'md-left'){
34260 if(box[0].size == 'md-right'){
34265 x : x + (this.unitWidth + this.gutter) * rand,
34272 getVerticalTwoBoxColPositions : function(x, y, box)
34276 if(box[0].size == 'xs'){
34280 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34284 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34298 x : x + (this.unitWidth + this.gutter) * 2,
34299 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34306 getVerticalThreeBoxColPositions : function(x, y, box)
34310 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34318 x : x + (this.unitWidth + this.gutter) * 1,
34323 x : x + (this.unitWidth + this.gutter) * 2,
34331 if(box[0].size == 'xs' && box[1].size == 'xs'){
34340 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34344 x : x + (this.unitWidth + this.gutter) * 1,
34358 x : x + (this.unitWidth + this.gutter) * 2,
34363 x : x + (this.unitWidth + this.gutter) * 2,
34364 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34371 getVerticalFourBoxColPositions : function(x, y, box)
34375 if(box[0].size == 'xs'){
34384 y : y + (this.unitHeight + this.gutter) * 1
34389 y : y + (this.unitHeight + this.gutter) * 2
34393 x : x + (this.unitWidth + this.gutter) * 1,
34407 x : x + (this.unitWidth + this.gutter) * 2,
34412 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34413 y : y + (this.unitHeight + this.gutter) * 1
34417 x : x + (this.unitWidth + this.gutter) * 2,
34418 y : y + (this.unitWidth + this.gutter) * 2
34425 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34429 if(box[0].size == 'md-left'){
34431 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34438 if(box[0].size == 'md-right'){
34440 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34441 y : minY + (this.unitWidth + this.gutter) * 1
34447 var rand = Math.floor(Math.random() * (4 - box[0].y));
34450 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34451 y : minY + (this.unitWidth + this.gutter) * rand
34458 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34462 if(box[0].size == 'xs'){
34465 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34470 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34471 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34479 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34484 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34485 y : minY + (this.unitWidth + this.gutter) * 2
34492 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34496 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34499 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34504 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34505 y : minY + (this.unitWidth + this.gutter) * 1
34509 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34510 y : minY + (this.unitWidth + this.gutter) * 2
34517 if(box[0].size == 'xs' && box[1].size == 'xs'){
34520 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34525 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34530 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34531 y : minY + (this.unitWidth + this.gutter) * 1
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) * 2
34549 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34550 y : minY + (this.unitWidth + this.gutter) * 2
34557 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34561 if(box[0].size == 'xs'){
34564 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34569 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34574 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),
34579 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34580 y : minY + (this.unitWidth + this.gutter) * 1
34588 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34593 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34594 y : minY + (this.unitWidth + this.gutter) * 2
34598 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34599 y : minY + (this.unitWidth + this.gutter) * 2
34603 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),
34604 y : minY + (this.unitWidth + this.gutter) * 2
34612 * remove a Masonry Brick
34613 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34615 removeBrick : function(brick_id)
34621 for (var i = 0; i<this.bricks.length; i++) {
34622 if (this.bricks[i].id == brick_id) {
34623 this.bricks.splice(i,1);
34624 this.el.dom.removeChild(Roo.get(brick_id).dom);
34631 * adds a Masonry Brick
34632 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34634 addBrick : function(cfg)
34636 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34637 //this.register(cn);
34638 cn.parentId = this.id;
34639 cn.render(this.el);
34644 * register a Masonry Brick
34645 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34648 register : function(brick)
34650 this.bricks.push(brick);
34651 brick.masonryId = this.id;
34655 * clear all the Masonry Brick
34657 clearAll : function()
34660 //this.getChildContainer().dom.innerHTML = "";
34661 this.el.dom.innerHTML = '';
34664 getSelected : function()
34666 if (!this.selectedBrick) {
34670 return this.selectedBrick;
34674 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34678 * register a Masonry Layout
34679 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34682 register : function(layout)
34684 this.groups[layout.id] = layout;
34687 * fetch a Masonry Layout based on the masonry layout ID
34688 * @param {string} the masonry layout to add
34689 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34692 get: function(layout_id) {
34693 if (typeof(this.groups[layout_id]) == 'undefined') {
34696 return this.groups[layout_id] ;
34708 * http://masonry.desandro.com
34710 * The idea is to render all the bricks based on vertical width...
34712 * The original code extends 'outlayer' - we might need to use that....
34718 * @class Roo.bootstrap.LayoutMasonryAuto
34719 * @extends Roo.bootstrap.Component
34720 * Bootstrap Layout Masonry class
34723 * Create a new Element
34724 * @param {Object} config The config object
34727 Roo.bootstrap.LayoutMasonryAuto = function(config){
34728 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34731 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34734 * @cfg {Boolean} isFitWidth - resize the width..
34736 isFitWidth : false, // options..
34738 * @cfg {Boolean} isOriginLeft = left align?
34740 isOriginLeft : true,
34742 * @cfg {Boolean} isOriginTop = top align?
34744 isOriginTop : false,
34746 * @cfg {Boolean} isLayoutInstant = no animation?
34748 isLayoutInstant : false, // needed?
34750 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34752 isResizingContainer : true,
34754 * @cfg {Number} columnWidth width of the columns
34760 * @cfg {Number} maxCols maximum number of columns
34765 * @cfg {Number} padHeight padding below box..
34771 * @cfg {Boolean} isAutoInitial defalut true
34774 isAutoInitial : true,
34780 initialColumnWidth : 0,
34781 currentSize : null,
34783 colYs : null, // array.
34790 bricks: null, //CompositeElement
34791 cols : 0, // array?
34792 // element : null, // wrapped now this.el
34793 _isLayoutInited : null,
34796 getAutoCreate : function(){
34800 cls: 'blog-masonary-wrapper ' + this.cls,
34802 cls : 'mas-boxes masonary'
34809 getChildContainer: function( )
34811 if (this.boxesEl) {
34812 return this.boxesEl;
34815 this.boxesEl = this.el.select('.mas-boxes').first();
34817 return this.boxesEl;
34821 initEvents : function()
34825 if(this.isAutoInitial){
34826 Roo.log('hook children rendered');
34827 this.on('childrenrendered', function() {
34828 Roo.log('children rendered');
34835 initial : function()
34837 this.reloadItems();
34839 this.currentSize = this.el.getBox(true);
34841 /// was window resize... - let's see if this works..
34842 Roo.EventManager.onWindowResize(this.resize, this);
34844 if(!this.isAutoInitial){
34849 this.layout.defer(500,this);
34852 reloadItems: function()
34854 this.bricks = this.el.select('.masonry-brick', true);
34856 this.bricks.each(function(b) {
34857 //Roo.log(b.getSize());
34858 if (!b.attr('originalwidth')) {
34859 b.attr('originalwidth', b.getSize().width);
34864 Roo.log(this.bricks.elements.length);
34867 resize : function()
34870 var cs = this.el.getBox(true);
34872 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34873 Roo.log("no change in with or X");
34876 this.currentSize = cs;
34880 layout : function()
34883 this._resetLayout();
34884 //this._manageStamps();
34886 // don't animate first layout
34887 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34888 this.layoutItems( isInstant );
34890 // flag for initalized
34891 this._isLayoutInited = true;
34894 layoutItems : function( isInstant )
34896 //var items = this._getItemsForLayout( this.items );
34897 // original code supports filtering layout items.. we just ignore it..
34899 this._layoutItems( this.bricks , isInstant );
34901 this._postLayout();
34903 _layoutItems : function ( items , isInstant)
34905 //this.fireEvent( 'layout', this, items );
34908 if ( !items || !items.elements.length ) {
34909 // no items, emit event with empty array
34914 items.each(function(item) {
34915 Roo.log("layout item");
34917 // get x/y object from method
34918 var position = this._getItemLayoutPosition( item );
34920 position.item = item;
34921 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34922 queue.push( position );
34925 this._processLayoutQueue( queue );
34927 /** Sets position of item in DOM
34928 * @param {Element} item
34929 * @param {Number} x - horizontal position
34930 * @param {Number} y - vertical position
34931 * @param {Boolean} isInstant - disables transitions
34933 _processLayoutQueue : function( queue )
34935 for ( var i=0, len = queue.length; i < len; i++ ) {
34936 var obj = queue[i];
34937 obj.item.position('absolute');
34938 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34944 * Any logic you want to do after each layout,
34945 * i.e. size the container
34947 _postLayout : function()
34949 this.resizeContainer();
34952 resizeContainer : function()
34954 if ( !this.isResizingContainer ) {
34957 var size = this._getContainerSize();
34959 this.el.setSize(size.width,size.height);
34960 this.boxesEl.setSize(size.width,size.height);
34966 _resetLayout : function()
34968 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34969 this.colWidth = this.el.getWidth();
34970 //this.gutter = this.el.getWidth();
34972 this.measureColumns();
34978 this.colYs.push( 0 );
34984 measureColumns : function()
34986 this.getContainerWidth();
34987 // if columnWidth is 0, default to outerWidth of first item
34988 if ( !this.columnWidth ) {
34989 var firstItem = this.bricks.first();
34990 Roo.log(firstItem);
34991 this.columnWidth = this.containerWidth;
34992 if (firstItem && firstItem.attr('originalwidth') ) {
34993 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34995 // columnWidth fall back to item of first element
34996 Roo.log("set column width?");
34997 this.initialColumnWidth = this.columnWidth ;
34999 // if first elem has no width, default to size of container
35004 if (this.initialColumnWidth) {
35005 this.columnWidth = this.initialColumnWidth;
35010 // column width is fixed at the top - however if container width get's smaller we should
35013 // this bit calcs how man columns..
35015 var columnWidth = this.columnWidth += this.gutter;
35017 // calculate columns
35018 var containerWidth = this.containerWidth + this.gutter;
35020 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35021 // fix rounding errors, typically with gutters
35022 var excess = columnWidth - containerWidth % columnWidth;
35025 // if overshoot is less than a pixel, round up, otherwise floor it
35026 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35027 cols = Math[ mathMethod ]( cols );
35028 this.cols = Math.max( cols, 1 );
35029 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35031 // padding positioning..
35032 var totalColWidth = this.cols * this.columnWidth;
35033 var padavail = this.containerWidth - totalColWidth;
35034 // so for 2 columns - we need 3 'pads'
35036 var padNeeded = (1+this.cols) * this.padWidth;
35038 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35040 this.columnWidth += padExtra
35041 //this.padWidth = Math.floor(padavail / ( this.cols));
35043 // adjust colum width so that padding is fixed??
35045 // we have 3 columns ... total = width * 3
35046 // we have X left over... that should be used by
35048 //if (this.expandC) {
35056 getContainerWidth : function()
35058 /* // container is parent if fit width
35059 var container = this.isFitWidth ? this.element.parentNode : this.element;
35060 // check that this.size and size are there
35061 // IE8 triggers resize on body size change, so they might not be
35063 var size = getSize( container ); //FIXME
35064 this.containerWidth = size && size.innerWidth; //FIXME
35067 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35071 _getItemLayoutPosition : function( item ) // what is item?
35073 // we resize the item to our columnWidth..
35075 item.setWidth(this.columnWidth);
35076 item.autoBoxAdjust = false;
35078 var sz = item.getSize();
35080 // how many columns does this brick span
35081 var remainder = this.containerWidth % this.columnWidth;
35083 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35084 // round if off by 1 pixel, otherwise use ceil
35085 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35086 colSpan = Math.min( colSpan, this.cols );
35088 // normally this should be '1' as we dont' currently allow multi width columns..
35090 var colGroup = this._getColGroup( colSpan );
35091 // get the minimum Y value from the columns
35092 var minimumY = Math.min.apply( Math, colGroup );
35093 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35095 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35097 // position the brick
35099 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35100 y: this.currentSize.y + minimumY + this.padHeight
35104 // apply setHeight to necessary columns
35105 var setHeight = minimumY + sz.height + this.padHeight;
35106 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35108 var setSpan = this.cols + 1 - colGroup.length;
35109 for ( var i = 0; i < setSpan; i++ ) {
35110 this.colYs[ shortColIndex + i ] = setHeight ;
35117 * @param {Number} colSpan - number of columns the element spans
35118 * @returns {Array} colGroup
35120 _getColGroup : function( colSpan )
35122 if ( colSpan < 2 ) {
35123 // if brick spans only one column, use all the column Ys
35128 // how many different places could this brick fit horizontally
35129 var groupCount = this.cols + 1 - colSpan;
35130 // for each group potential horizontal position
35131 for ( var i = 0; i < groupCount; i++ ) {
35132 // make an array of colY values for that one group
35133 var groupColYs = this.colYs.slice( i, i + colSpan );
35134 // and get the max value of the array
35135 colGroup[i] = Math.max.apply( Math, groupColYs );
35140 _manageStamp : function( stamp )
35142 var stampSize = stamp.getSize();
35143 var offset = stamp.getBox();
35144 // get the columns that this stamp affects
35145 var firstX = this.isOriginLeft ? offset.x : offset.right;
35146 var lastX = firstX + stampSize.width;
35147 var firstCol = Math.floor( firstX / this.columnWidth );
35148 firstCol = Math.max( 0, firstCol );
35150 var lastCol = Math.floor( lastX / this.columnWidth );
35151 // lastCol should not go over if multiple of columnWidth #425
35152 lastCol -= lastX % this.columnWidth ? 0 : 1;
35153 lastCol = Math.min( this.cols - 1, lastCol );
35155 // set colYs to bottom of the stamp
35156 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35159 for ( var i = firstCol; i <= lastCol; i++ ) {
35160 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35165 _getContainerSize : function()
35167 this.maxY = Math.max.apply( Math, this.colYs );
35172 if ( this.isFitWidth ) {
35173 size.width = this._getContainerFitWidth();
35179 _getContainerFitWidth : function()
35181 var unusedCols = 0;
35182 // count unused columns
35185 if ( this.colYs[i] !== 0 ) {
35190 // fit container to columns that have been used
35191 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35194 needsResizeLayout : function()
35196 var previousWidth = this.containerWidth;
35197 this.getContainerWidth();
35198 return previousWidth !== this.containerWidth;
35213 * @class Roo.bootstrap.MasonryBrick
35214 * @extends Roo.bootstrap.Component
35215 * Bootstrap MasonryBrick class
35218 * Create a new MasonryBrick
35219 * @param {Object} config The config object
35222 Roo.bootstrap.MasonryBrick = function(config){
35224 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35226 Roo.bootstrap.MasonryBrick.register(this);
35232 * When a MasonryBrick is clcik
35233 * @param {Roo.bootstrap.MasonryBrick} this
35234 * @param {Roo.EventObject} e
35240 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35243 * @cfg {String} title
35247 * @cfg {String} html
35251 * @cfg {String} bgimage
35255 * @cfg {String} videourl
35259 * @cfg {String} cls
35263 * @cfg {String} href
35267 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35272 * @cfg {String} placetitle (center|bottom)
35277 * @cfg {Boolean} isFitContainer defalut true
35279 isFitContainer : true,
35282 * @cfg {Boolean} preventDefault defalut false
35284 preventDefault : false,
35287 * @cfg {Boolean} inverse defalut false
35289 maskInverse : false,
35291 getAutoCreate : function()
35293 if(!this.isFitContainer){
35294 return this.getSplitAutoCreate();
35297 var cls = 'masonry-brick masonry-brick-full';
35299 if(this.href.length){
35300 cls += ' masonry-brick-link';
35303 if(this.bgimage.length){
35304 cls += ' masonry-brick-image';
35307 if(this.maskInverse){
35308 cls += ' mask-inverse';
35311 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35312 cls += ' enable-mask';
35316 cls += ' masonry-' + this.size + '-brick';
35319 if(this.placetitle.length){
35321 switch (this.placetitle) {
35323 cls += ' masonry-center-title';
35326 cls += ' masonry-bottom-title';
35333 if(!this.html.length && !this.bgimage.length){
35334 cls += ' masonry-center-title';
35337 if(!this.html.length && this.bgimage.length){
35338 cls += ' masonry-bottom-title';
35343 cls += ' ' + this.cls;
35347 tag: (this.href.length) ? 'a' : 'div',
35352 cls: 'masonry-brick-mask'
35356 cls: 'masonry-brick-paragraph',
35362 if(this.href.length){
35363 cfg.href = this.href;
35366 var cn = cfg.cn[1].cn;
35368 if(this.title.length){
35371 cls: 'masonry-brick-title',
35376 if(this.html.length){
35379 cls: 'masonry-brick-text',
35384 if (!this.title.length && !this.html.length) {
35385 cfg.cn[1].cls += ' hide';
35388 if(this.bgimage.length){
35391 cls: 'masonry-brick-image-view',
35396 if(this.videourl.length){
35397 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35398 // youtube support only?
35401 cls: 'masonry-brick-image-view',
35404 allowfullscreen : true
35412 getSplitAutoCreate : function()
35414 var cls = 'masonry-brick masonry-brick-split';
35416 if(this.href.length){
35417 cls += ' masonry-brick-link';
35420 if(this.bgimage.length){
35421 cls += ' masonry-brick-image';
35425 cls += ' masonry-' + this.size + '-brick';
35428 switch (this.placetitle) {
35430 cls += ' masonry-center-title';
35433 cls += ' masonry-bottom-title';
35436 if(!this.bgimage.length){
35437 cls += ' masonry-center-title';
35440 if(this.bgimage.length){
35441 cls += ' masonry-bottom-title';
35447 cls += ' ' + this.cls;
35451 tag: (this.href.length) ? 'a' : 'div',
35456 cls: 'masonry-brick-split-head',
35460 cls: 'masonry-brick-paragraph',
35467 cls: 'masonry-brick-split-body',
35473 if(this.href.length){
35474 cfg.href = this.href;
35477 if(this.title.length){
35478 cfg.cn[0].cn[0].cn.push({
35480 cls: 'masonry-brick-title',
35485 if(this.html.length){
35486 cfg.cn[1].cn.push({
35488 cls: 'masonry-brick-text',
35493 if(this.bgimage.length){
35494 cfg.cn[0].cn.push({
35496 cls: 'masonry-brick-image-view',
35501 if(this.videourl.length){
35502 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35503 // youtube support only?
35504 cfg.cn[0].cn.cn.push({
35506 cls: 'masonry-brick-image-view',
35509 allowfullscreen : true
35516 initEvents: function()
35518 switch (this.size) {
35551 this.el.on('touchstart', this.onTouchStart, this);
35552 this.el.on('touchmove', this.onTouchMove, this);
35553 this.el.on('touchend', this.onTouchEnd, this);
35554 this.el.on('contextmenu', this.onContextMenu, this);
35556 this.el.on('mouseenter' ,this.enter, this);
35557 this.el.on('mouseleave', this.leave, this);
35558 this.el.on('click', this.onClick, this);
35561 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35562 this.parent().bricks.push(this);
35567 onClick: function(e, el)
35569 var time = this.endTimer - this.startTimer;
35570 // Roo.log(e.preventDefault());
35573 e.preventDefault();
35578 if(!this.preventDefault){
35582 e.preventDefault();
35584 if (this.activeClass != '') {
35585 this.selectBrick();
35588 this.fireEvent('click', this, e);
35591 enter: function(e, el)
35593 e.preventDefault();
35595 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35599 if(this.bgimage.length && this.html.length){
35600 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35604 leave: function(e, el)
35606 e.preventDefault();
35608 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35612 if(this.bgimage.length && this.html.length){
35613 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35617 onTouchStart: function(e, el)
35619 // e.preventDefault();
35621 this.touchmoved = false;
35623 if(!this.isFitContainer){
35627 if(!this.bgimage.length || !this.html.length){
35631 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35633 this.timer = new Date().getTime();
35637 onTouchMove: function(e, el)
35639 this.touchmoved = true;
35642 onContextMenu : function(e,el)
35644 e.preventDefault();
35645 e.stopPropagation();
35649 onTouchEnd: function(e, el)
35651 // e.preventDefault();
35653 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35660 if(!this.bgimage.length || !this.html.length){
35662 if(this.href.length){
35663 window.location.href = this.href;
35669 if(!this.isFitContainer){
35673 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35675 window.location.href = this.href;
35678 //selection on single brick only
35679 selectBrick : function() {
35681 if (!this.parentId) {
35685 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35686 var index = m.selectedBrick.indexOf(this.id);
35689 m.selectedBrick.splice(index,1);
35690 this.el.removeClass(this.activeClass);
35694 for(var i = 0; i < m.selectedBrick.length; i++) {
35695 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35696 b.el.removeClass(b.activeClass);
35699 m.selectedBrick = [];
35701 m.selectedBrick.push(this.id);
35702 this.el.addClass(this.activeClass);
35706 isSelected : function(){
35707 return this.el.hasClass(this.activeClass);
35712 Roo.apply(Roo.bootstrap.MasonryBrick, {
35715 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35717 * register a Masonry Brick
35718 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35721 register : function(brick)
35723 //this.groups[brick.id] = brick;
35724 this.groups.add(brick.id, brick);
35727 * fetch a masonry brick based on the masonry brick ID
35728 * @param {string} the masonry brick to add
35729 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35732 get: function(brick_id)
35734 // if (typeof(this.groups[brick_id]) == 'undefined') {
35737 // return this.groups[brick_id] ;
35739 if(this.groups.key(brick_id)) {
35740 return this.groups.key(brick_id);
35758 * @class Roo.bootstrap.Brick
35759 * @extends Roo.bootstrap.Component
35760 * Bootstrap Brick class
35763 * Create a new Brick
35764 * @param {Object} config The config object
35767 Roo.bootstrap.Brick = function(config){
35768 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35774 * When a Brick is click
35775 * @param {Roo.bootstrap.Brick} this
35776 * @param {Roo.EventObject} e
35782 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35785 * @cfg {String} title
35789 * @cfg {String} html
35793 * @cfg {String} bgimage
35797 * @cfg {String} cls
35801 * @cfg {String} href
35805 * @cfg {String} video
35809 * @cfg {Boolean} square
35813 getAutoCreate : function()
35815 var cls = 'roo-brick';
35817 if(this.href.length){
35818 cls += ' roo-brick-link';
35821 if(this.bgimage.length){
35822 cls += ' roo-brick-image';
35825 if(!this.html.length && !this.bgimage.length){
35826 cls += ' roo-brick-center-title';
35829 if(!this.html.length && this.bgimage.length){
35830 cls += ' roo-brick-bottom-title';
35834 cls += ' ' + this.cls;
35838 tag: (this.href.length) ? 'a' : 'div',
35843 cls: 'roo-brick-paragraph',
35849 if(this.href.length){
35850 cfg.href = this.href;
35853 var cn = cfg.cn[0].cn;
35855 if(this.title.length){
35858 cls: 'roo-brick-title',
35863 if(this.html.length){
35866 cls: 'roo-brick-text',
35873 if(this.bgimage.length){
35876 cls: 'roo-brick-image-view',
35884 initEvents: function()
35886 if(this.title.length || this.html.length){
35887 this.el.on('mouseenter' ,this.enter, this);
35888 this.el.on('mouseleave', this.leave, this);
35891 Roo.EventManager.onWindowResize(this.resize, this);
35893 if(this.bgimage.length){
35894 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35895 this.imageEl.on('load', this.onImageLoad, this);
35902 onImageLoad : function()
35907 resize : function()
35909 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35911 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35913 if(this.bgimage.length){
35914 var image = this.el.select('.roo-brick-image-view', true).first();
35916 image.setWidth(paragraph.getWidth());
35919 image.setHeight(paragraph.getWidth());
35922 this.el.setHeight(image.getHeight());
35923 paragraph.setHeight(image.getHeight());
35929 enter: function(e, el)
35931 e.preventDefault();
35933 if(this.bgimage.length){
35934 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35935 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35939 leave: function(e, el)
35941 e.preventDefault();
35943 if(this.bgimage.length){
35944 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35945 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35960 * @class Roo.bootstrap.NumberField
35961 * @extends Roo.bootstrap.Input
35962 * Bootstrap NumberField class
35968 * Create a new NumberField
35969 * @param {Object} config The config object
35972 Roo.bootstrap.NumberField = function(config){
35973 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35976 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35979 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35981 allowDecimals : true,
35983 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35985 decimalSeparator : ".",
35987 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35989 decimalPrecision : 2,
35991 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35993 allowNegative : true,
35996 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36000 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36002 minValue : Number.NEGATIVE_INFINITY,
36004 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36006 maxValue : Number.MAX_VALUE,
36008 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36010 minText : "The minimum value for this field is {0}",
36012 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36014 maxText : "The maximum value for this field is {0}",
36016 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36017 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36019 nanText : "{0} is not a valid number",
36021 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36023 thousandsDelimiter : false,
36025 * @cfg {String} valueAlign alignment of value
36027 valueAlign : "left",
36029 getAutoCreate : function()
36031 var hiddenInput = {
36035 cls: 'hidden-number-input'
36039 hiddenInput.name = this.name;
36044 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36046 this.name = hiddenInput.name;
36048 if(cfg.cn.length > 0) {
36049 cfg.cn.push(hiddenInput);
36056 initEvents : function()
36058 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36060 var allowed = "0123456789";
36062 if(this.allowDecimals){
36063 allowed += this.decimalSeparator;
36066 if(this.allowNegative){
36070 if(this.thousandsDelimiter) {
36074 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36076 var keyPress = function(e){
36078 var k = e.getKey();
36080 var c = e.getCharCode();
36083 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36084 allowed.indexOf(String.fromCharCode(c)) === -1
36090 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36094 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36099 this.el.on("keypress", keyPress, this);
36102 validateValue : function(value)
36105 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36109 var num = this.parseValue(value);
36112 this.markInvalid(String.format(this.nanText, value));
36116 if(num < this.minValue){
36117 this.markInvalid(String.format(this.minText, this.minValue));
36121 if(num > this.maxValue){
36122 this.markInvalid(String.format(this.maxText, this.maxValue));
36129 getValue : function()
36131 var v = this.hiddenEl().getValue();
36133 return this.fixPrecision(this.parseValue(v));
36136 parseValue : function(value)
36138 if(this.thousandsDelimiter) {
36140 r = new RegExp(",", "g");
36141 value = value.replace(r, "");
36144 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36145 return isNaN(value) ? '' : value;
36148 fixPrecision : function(value)
36150 if(this.thousandsDelimiter) {
36152 r = new RegExp(",", "g");
36153 value = value.replace(r, "");
36156 var nan = isNaN(value);
36158 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36159 return nan ? '' : value;
36161 return parseFloat(value).toFixed(this.decimalPrecision);
36164 setValue : function(v)
36166 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36172 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36174 this.inputEl().dom.value = (v == '') ? '' :
36175 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36177 if(!this.allowZero && v === '0') {
36178 this.hiddenEl().dom.value = '';
36179 this.inputEl().dom.value = '';
36186 decimalPrecisionFcn : function(v)
36188 return Math.floor(v);
36191 beforeBlur : function()
36193 var v = this.parseValue(this.getRawValue());
36195 if(v || v === 0 || v === ''){
36200 hiddenEl : function()
36202 return this.el.select('input.hidden-number-input',true).first();
36214 * @class Roo.bootstrap.DocumentSlider
36215 * @extends Roo.bootstrap.Component
36216 * Bootstrap DocumentSlider class
36219 * Create a new DocumentViewer
36220 * @param {Object} config The config object
36223 Roo.bootstrap.DocumentSlider = function(config){
36224 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36231 * Fire after initEvent
36232 * @param {Roo.bootstrap.DocumentSlider} this
36237 * Fire after update
36238 * @param {Roo.bootstrap.DocumentSlider} this
36244 * @param {Roo.bootstrap.DocumentSlider} this
36250 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36256 getAutoCreate : function()
36260 cls : 'roo-document-slider',
36264 cls : 'roo-document-slider-header',
36268 cls : 'roo-document-slider-header-title'
36274 cls : 'roo-document-slider-body',
36278 cls : 'roo-document-slider-prev',
36282 cls : 'fa fa-chevron-left'
36288 cls : 'roo-document-slider-thumb',
36292 cls : 'roo-document-slider-image'
36298 cls : 'roo-document-slider-next',
36302 cls : 'fa fa-chevron-right'
36314 initEvents : function()
36316 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36317 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36319 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36320 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36322 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36323 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36325 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36326 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36328 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36329 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36331 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36332 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36334 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36335 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36337 this.thumbEl.on('click', this.onClick, this);
36339 this.prevIndicator.on('click', this.prev, this);
36341 this.nextIndicator.on('click', this.next, this);
36345 initial : function()
36347 if(this.files.length){
36348 this.indicator = 1;
36352 this.fireEvent('initial', this);
36355 update : function()
36357 this.imageEl.attr('src', this.files[this.indicator - 1]);
36359 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36361 this.prevIndicator.show();
36363 if(this.indicator == 1){
36364 this.prevIndicator.hide();
36367 this.nextIndicator.show();
36369 if(this.indicator == this.files.length){
36370 this.nextIndicator.hide();
36373 this.thumbEl.scrollTo('top');
36375 this.fireEvent('update', this);
36378 onClick : function(e)
36380 e.preventDefault();
36382 this.fireEvent('click', this);
36387 e.preventDefault();
36389 this.indicator = Math.max(1, this.indicator - 1);
36396 e.preventDefault();
36398 this.indicator = Math.min(this.files.length, this.indicator + 1);
36412 * @class Roo.bootstrap.RadioSet
36413 * @extends Roo.bootstrap.Input
36414 * Bootstrap RadioSet class
36415 * @cfg {String} indicatorpos (left|right) default left
36416 * @cfg {Boolean} inline (true|false) inline the element (default true)
36417 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36419 * Create a new RadioSet
36420 * @param {Object} config The config object
36423 Roo.bootstrap.RadioSet = function(config){
36425 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36429 Roo.bootstrap.RadioSet.register(this);
36434 * Fires when the element is checked or unchecked.
36435 * @param {Roo.bootstrap.RadioSet} this This radio
36436 * @param {Roo.bootstrap.Radio} item The checked item
36441 * Fires when the element is click.
36442 * @param {Roo.bootstrap.RadioSet} this This radio set
36443 * @param {Roo.bootstrap.Radio} item The checked item
36444 * @param {Roo.EventObject} e The event object
36451 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36459 indicatorpos : 'left',
36461 getAutoCreate : function()
36465 cls : 'roo-radio-set-label',
36469 html : this.fieldLabel
36473 if (Roo.bootstrap.version == 3) {
36476 if(this.indicatorpos == 'left'){
36479 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36480 tooltip : 'This field is required'
36485 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36486 tooltip : 'This field is required'
36492 cls : 'roo-radio-set-items'
36495 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36497 if (align === 'left' && this.fieldLabel.length) {
36500 cls : "roo-radio-set-right",
36506 if(this.labelWidth > 12){
36507 label.style = "width: " + this.labelWidth + 'px';
36510 if(this.labelWidth < 13 && this.labelmd == 0){
36511 this.labelmd = this.labelWidth;
36514 if(this.labellg > 0){
36515 label.cls += ' col-lg-' + this.labellg;
36516 items.cls += ' col-lg-' + (12 - this.labellg);
36519 if(this.labelmd > 0){
36520 label.cls += ' col-md-' + this.labelmd;
36521 items.cls += ' col-md-' + (12 - this.labelmd);
36524 if(this.labelsm > 0){
36525 label.cls += ' col-sm-' + this.labelsm;
36526 items.cls += ' col-sm-' + (12 - this.labelsm);
36529 if(this.labelxs > 0){
36530 label.cls += ' col-xs-' + this.labelxs;
36531 items.cls += ' col-xs-' + (12 - this.labelxs);
36537 cls : 'roo-radio-set',
36541 cls : 'roo-radio-set-input',
36544 value : this.value ? this.value : ''
36551 if(this.weight.length){
36552 cfg.cls += ' roo-radio-' + this.weight;
36556 cfg.cls += ' roo-radio-set-inline';
36560 ['xs','sm','md','lg'].map(function(size){
36561 if (settings[size]) {
36562 cfg.cls += ' col-' + size + '-' + settings[size];
36570 initEvents : function()
36572 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36573 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36575 if(!this.fieldLabel.length){
36576 this.labelEl.hide();
36579 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36580 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36582 this.indicator = this.indicatorEl();
36584 if(this.indicator){
36585 this.indicator.addClass('invisible');
36588 this.originalValue = this.getValue();
36592 inputEl: function ()
36594 return this.el.select('.roo-radio-set-input', true).first();
36597 getChildContainer : function()
36599 return this.itemsEl;
36602 register : function(item)
36604 this.radioes.push(item);
36608 validate : function()
36610 if(this.getVisibilityEl().hasClass('hidden')){
36616 Roo.each(this.radioes, function(i){
36625 if(this.allowBlank) {
36629 if(this.disabled || valid){
36634 this.markInvalid();
36639 markValid : function()
36641 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36642 this.indicatorEl().removeClass('visible');
36643 this.indicatorEl().addClass('invisible');
36647 if (Roo.bootstrap.version == 3) {
36648 this.el.removeClass([this.invalidClass, this.validClass]);
36649 this.el.addClass(this.validClass);
36651 this.el.removeClass(['is-invalid','is-valid']);
36652 this.el.addClass(['is-valid']);
36654 this.fireEvent('valid', this);
36657 markInvalid : function(msg)
36659 if(this.allowBlank || this.disabled){
36663 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36664 this.indicatorEl().removeClass('invisible');
36665 this.indicatorEl().addClass('visible');
36667 if (Roo.bootstrap.version == 3) {
36668 this.el.removeClass([this.invalidClass, this.validClass]);
36669 this.el.addClass(this.invalidClass);
36671 this.el.removeClass(['is-invalid','is-valid']);
36672 this.el.addClass(['is-invalid']);
36675 this.fireEvent('invalid', this, msg);
36679 setValue : function(v, suppressEvent)
36681 if(this.value === v){
36688 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36691 Roo.each(this.radioes, function(i){
36693 i.el.removeClass('checked');
36696 Roo.each(this.radioes, function(i){
36698 if(i.value === v || i.value.toString() === v.toString()){
36700 i.el.addClass('checked');
36702 if(suppressEvent !== true){
36703 this.fireEvent('check', this, i);
36714 clearInvalid : function(){
36716 if(!this.el || this.preventMark){
36720 this.el.removeClass([this.invalidClass]);
36722 this.fireEvent('valid', this);
36727 Roo.apply(Roo.bootstrap.RadioSet, {
36731 register : function(set)
36733 this.groups[set.name] = set;
36736 get: function(name)
36738 if (typeof(this.groups[name]) == 'undefined') {
36742 return this.groups[name] ;
36748 * Ext JS Library 1.1.1
36749 * Copyright(c) 2006-2007, Ext JS, LLC.
36751 * Originally Released Under LGPL - original licence link has changed is not relivant.
36754 * <script type="text/javascript">
36759 * @class Roo.bootstrap.SplitBar
36760 * @extends Roo.util.Observable
36761 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36765 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36766 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36767 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36768 split.minSize = 100;
36769 split.maxSize = 600;
36770 split.animate = true;
36771 split.on('moved', splitterMoved);
36774 * Create a new SplitBar
36775 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36776 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36777 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36778 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36779 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36780 position of the SplitBar).
36782 Roo.bootstrap.SplitBar = function(cfg){
36787 // dragElement : elm
36788 // resizingElement: el,
36790 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36791 // placement : Roo.bootstrap.SplitBar.LEFT ,
36792 // existingProxy ???
36795 this.el = Roo.get(cfg.dragElement, true);
36796 this.el.dom.unselectable = "on";
36798 this.resizingEl = Roo.get(cfg.resizingElement, true);
36802 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36803 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36806 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36809 * The minimum size of the resizing element. (Defaults to 0)
36815 * The maximum size of the resizing element. (Defaults to 2000)
36818 this.maxSize = 2000;
36821 * Whether to animate the transition to the new size
36824 this.animate = false;
36827 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36830 this.useShim = false;
36835 if(!cfg.existingProxy){
36837 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36839 this.proxy = Roo.get(cfg.existingProxy).dom;
36842 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36845 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36848 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36851 this.dragSpecs = {};
36854 * @private The adapter to use to positon and resize elements
36856 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36857 this.adapter.init(this);
36859 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36861 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36862 this.el.addClass("roo-splitbar-h");
36865 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36866 this.el.addClass("roo-splitbar-v");
36872 * Fires when the splitter is moved (alias for {@link #event-moved})
36873 * @param {Roo.bootstrap.SplitBar} this
36874 * @param {Number} newSize the new width or height
36879 * Fires when the splitter is moved
36880 * @param {Roo.bootstrap.SplitBar} this
36881 * @param {Number} newSize the new width or height
36885 * @event beforeresize
36886 * Fires before the splitter is dragged
36887 * @param {Roo.bootstrap.SplitBar} this
36889 "beforeresize" : true,
36891 "beforeapply" : true
36894 Roo.util.Observable.call(this);
36897 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36898 onStartProxyDrag : function(x, y){
36899 this.fireEvent("beforeresize", this);
36901 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36903 o.enableDisplayMode("block");
36904 // all splitbars share the same overlay
36905 Roo.bootstrap.SplitBar.prototype.overlay = o;
36907 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36908 this.overlay.show();
36909 Roo.get(this.proxy).setDisplayed("block");
36910 var size = this.adapter.getElementSize(this);
36911 this.activeMinSize = this.getMinimumSize();;
36912 this.activeMaxSize = this.getMaximumSize();;
36913 var c1 = size - this.activeMinSize;
36914 var c2 = Math.max(this.activeMaxSize - size, 0);
36915 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36916 this.dd.resetConstraints();
36917 this.dd.setXConstraint(
36918 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36919 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36921 this.dd.setYConstraint(0, 0);
36923 this.dd.resetConstraints();
36924 this.dd.setXConstraint(0, 0);
36925 this.dd.setYConstraint(
36926 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36927 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36930 this.dragSpecs.startSize = size;
36931 this.dragSpecs.startPoint = [x, y];
36932 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36936 * @private Called after the drag operation by the DDProxy
36938 onEndProxyDrag : function(e){
36939 Roo.get(this.proxy).setDisplayed(false);
36940 var endPoint = Roo.lib.Event.getXY(e);
36942 this.overlay.hide();
36945 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36946 newSize = this.dragSpecs.startSize +
36947 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36948 endPoint[0] - this.dragSpecs.startPoint[0] :
36949 this.dragSpecs.startPoint[0] - endPoint[0]
36952 newSize = this.dragSpecs.startSize +
36953 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36954 endPoint[1] - this.dragSpecs.startPoint[1] :
36955 this.dragSpecs.startPoint[1] - endPoint[1]
36958 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36959 if(newSize != this.dragSpecs.startSize){
36960 if(this.fireEvent('beforeapply', this, newSize) !== false){
36961 this.adapter.setElementSize(this, newSize);
36962 this.fireEvent("moved", this, newSize);
36963 this.fireEvent("resize", this, newSize);
36969 * Get the adapter this SplitBar uses
36970 * @return The adapter object
36972 getAdapter : function(){
36973 return this.adapter;
36977 * Set the adapter this SplitBar uses
36978 * @param {Object} adapter A SplitBar adapter object
36980 setAdapter : function(adapter){
36981 this.adapter = adapter;
36982 this.adapter.init(this);
36986 * Gets the minimum size for the resizing element
36987 * @return {Number} The minimum size
36989 getMinimumSize : function(){
36990 return this.minSize;
36994 * Sets the minimum size for the resizing element
36995 * @param {Number} minSize The minimum size
36997 setMinimumSize : function(minSize){
36998 this.minSize = minSize;
37002 * Gets the maximum size for the resizing element
37003 * @return {Number} The maximum size
37005 getMaximumSize : function(){
37006 return this.maxSize;
37010 * Sets the maximum size for the resizing element
37011 * @param {Number} maxSize The maximum size
37013 setMaximumSize : function(maxSize){
37014 this.maxSize = maxSize;
37018 * Sets the initialize size for the resizing element
37019 * @param {Number} size The initial size
37021 setCurrentSize : function(size){
37022 var oldAnimate = this.animate;
37023 this.animate = false;
37024 this.adapter.setElementSize(this, size);
37025 this.animate = oldAnimate;
37029 * Destroy this splitbar.
37030 * @param {Boolean} removeEl True to remove the element
37032 destroy : function(removeEl){
37034 this.shim.remove();
37037 this.proxy.parentNode.removeChild(this.proxy);
37045 * @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.
37047 Roo.bootstrap.SplitBar.createProxy = function(dir){
37048 var proxy = new Roo.Element(document.createElement("div"));
37049 proxy.unselectable();
37050 var cls = 'roo-splitbar-proxy';
37051 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37052 document.body.appendChild(proxy.dom);
37057 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37058 * Default Adapter. It assumes the splitter and resizing element are not positioned
37059 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37061 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37064 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37065 // do nothing for now
37066 init : function(s){
37070 * Called before drag operations to get the current size of the resizing element.
37071 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37073 getElementSize : function(s){
37074 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37075 return s.resizingEl.getWidth();
37077 return s.resizingEl.getHeight();
37082 * Called after drag operations to set the size of the resizing element.
37083 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37084 * @param {Number} newSize The new size to set
37085 * @param {Function} onComplete A function to be invoked when resizing is complete
37087 setElementSize : function(s, newSize, onComplete){
37088 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37090 s.resizingEl.setWidth(newSize);
37092 onComplete(s, newSize);
37095 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37100 s.resizingEl.setHeight(newSize);
37102 onComplete(s, newSize);
37105 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37112 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37113 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37114 * Adapter that moves the splitter element to align with the resized sizing element.
37115 * Used with an absolute positioned SplitBar.
37116 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37117 * document.body, make sure you assign an id to the body element.
37119 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37120 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37121 this.container = Roo.get(container);
37124 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37125 init : function(s){
37126 this.basic.init(s);
37129 getElementSize : function(s){
37130 return this.basic.getElementSize(s);
37133 setElementSize : function(s, newSize, onComplete){
37134 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37137 moveSplitter : function(s){
37138 var yes = Roo.bootstrap.SplitBar;
37139 switch(s.placement){
37141 s.el.setX(s.resizingEl.getRight());
37144 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37147 s.el.setY(s.resizingEl.getBottom());
37150 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37157 * Orientation constant - Create a vertical SplitBar
37161 Roo.bootstrap.SplitBar.VERTICAL = 1;
37164 * Orientation constant - Create a horizontal SplitBar
37168 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37171 * Placement constant - The resizing element is to the left of the splitter element
37175 Roo.bootstrap.SplitBar.LEFT = 1;
37178 * Placement constant - The resizing element is to the right of the splitter element
37182 Roo.bootstrap.SplitBar.RIGHT = 2;
37185 * Placement constant - The resizing element is positioned above the splitter element
37189 Roo.bootstrap.SplitBar.TOP = 3;
37192 * Placement constant - The resizing element is positioned under splitter element
37196 Roo.bootstrap.SplitBar.BOTTOM = 4;
37197 Roo.namespace("Roo.bootstrap.layout");/*
37199 * Ext JS Library 1.1.1
37200 * Copyright(c) 2006-2007, Ext JS, LLC.
37202 * Originally Released Under LGPL - original licence link has changed is not relivant.
37205 * <script type="text/javascript">
37209 * @class Roo.bootstrap.layout.Manager
37210 * @extends Roo.bootstrap.Component
37211 * Base class for layout managers.
37213 Roo.bootstrap.layout.Manager = function(config)
37215 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37221 /** false to disable window resize monitoring @type Boolean */
37222 this.monitorWindowResize = true;
37227 * Fires when a layout is performed.
37228 * @param {Roo.LayoutManager} this
37232 * @event regionresized
37233 * Fires when the user resizes a region.
37234 * @param {Roo.LayoutRegion} region The resized region
37235 * @param {Number} newSize The new size (width for east/west, height for north/south)
37237 "regionresized" : true,
37239 * @event regioncollapsed
37240 * Fires when a region is collapsed.
37241 * @param {Roo.LayoutRegion} region The collapsed region
37243 "regioncollapsed" : true,
37245 * @event regionexpanded
37246 * Fires when a region is expanded.
37247 * @param {Roo.LayoutRegion} region The expanded region
37249 "regionexpanded" : true
37251 this.updating = false;
37254 this.el = Roo.get(config.el);
37260 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37265 monitorWindowResize : true,
37271 onRender : function(ct, position)
37274 this.el = Roo.get(ct);
37277 //this.fireEvent('render',this);
37281 initEvents: function()
37285 // ie scrollbar fix
37286 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37287 document.body.scroll = "no";
37288 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37289 this.el.position('relative');
37291 this.id = this.el.id;
37292 this.el.addClass("roo-layout-container");
37293 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37294 if(this.el.dom != document.body ) {
37295 this.el.on('resize', this.layout,this);
37296 this.el.on('show', this.layout,this);
37302 * Returns true if this layout is currently being updated
37303 * @return {Boolean}
37305 isUpdating : function(){
37306 return this.updating;
37310 * Suspend the LayoutManager from doing auto-layouts while
37311 * making multiple add or remove calls
37313 beginUpdate : function(){
37314 this.updating = true;
37318 * Restore auto-layouts and optionally disable the manager from performing a layout
37319 * @param {Boolean} noLayout true to disable a layout update
37321 endUpdate : function(noLayout){
37322 this.updating = false;
37328 layout: function(){
37332 onRegionResized : function(region, newSize){
37333 this.fireEvent("regionresized", region, newSize);
37337 onRegionCollapsed : function(region){
37338 this.fireEvent("regioncollapsed", region);
37341 onRegionExpanded : function(region){
37342 this.fireEvent("regionexpanded", region);
37346 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37347 * performs box-model adjustments.
37348 * @return {Object} The size as an object {width: (the width), height: (the height)}
37350 getViewSize : function()
37353 if(this.el.dom != document.body){
37354 size = this.el.getSize();
37356 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37358 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37359 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37364 * Returns the Element this layout is bound to.
37365 * @return {Roo.Element}
37367 getEl : function(){
37372 * Returns the specified region.
37373 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37374 * @return {Roo.LayoutRegion}
37376 getRegion : function(target){
37377 return this.regions[target.toLowerCase()];
37380 onWindowResize : function(){
37381 if(this.monitorWindowResize){
37388 * Ext JS Library 1.1.1
37389 * Copyright(c) 2006-2007, Ext JS, LLC.
37391 * Originally Released Under LGPL - original licence link has changed is not relivant.
37394 * <script type="text/javascript">
37397 * @class Roo.bootstrap.layout.Border
37398 * @extends Roo.bootstrap.layout.Manager
37399 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37400 * please see: examples/bootstrap/nested.html<br><br>
37402 <b>The container the layout is rendered into can be either the body element or any other element.
37403 If it is not the body element, the container needs to either be an absolute positioned element,
37404 or you will need to add "position:relative" to the css of the container. You will also need to specify
37405 the container size if it is not the body element.</b>
37408 * Create a new Border
37409 * @param {Object} config Configuration options
37411 Roo.bootstrap.layout.Border = function(config){
37412 config = config || {};
37413 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37417 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37418 if(config[region]){
37419 config[region].region = region;
37420 this.addRegion(config[region]);
37426 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37428 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37430 parent : false, // this might point to a 'nest' or a ???
37433 * Creates and adds a new region if it doesn't already exist.
37434 * @param {String} target The target region key (north, south, east, west or center).
37435 * @param {Object} config The regions config object
37436 * @return {BorderLayoutRegion} The new region
37438 addRegion : function(config)
37440 if(!this.regions[config.region]){
37441 var r = this.factory(config);
37442 this.bindRegion(r);
37444 return this.regions[config.region];
37448 bindRegion : function(r){
37449 this.regions[r.config.region] = r;
37451 r.on("visibilitychange", this.layout, this);
37452 r.on("paneladded", this.layout, this);
37453 r.on("panelremoved", this.layout, this);
37454 r.on("invalidated", this.layout, this);
37455 r.on("resized", this.onRegionResized, this);
37456 r.on("collapsed", this.onRegionCollapsed, this);
37457 r.on("expanded", this.onRegionExpanded, this);
37461 * Performs a layout update.
37463 layout : function()
37465 if(this.updating) {
37469 // render all the rebions if they have not been done alreayd?
37470 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37471 if(this.regions[region] && !this.regions[region].bodyEl){
37472 this.regions[region].onRender(this.el)
37476 var size = this.getViewSize();
37477 var w = size.width;
37478 var h = size.height;
37483 //var x = 0, y = 0;
37485 var rs = this.regions;
37486 var north = rs["north"];
37487 var south = rs["south"];
37488 var west = rs["west"];
37489 var east = rs["east"];
37490 var center = rs["center"];
37491 //if(this.hideOnLayout){ // not supported anymore
37492 //c.el.setStyle("display", "none");
37494 if(north && north.isVisible()){
37495 var b = north.getBox();
37496 var m = north.getMargins();
37497 b.width = w - (m.left+m.right);
37500 centerY = b.height + b.y + m.bottom;
37501 centerH -= centerY;
37502 north.updateBox(this.safeBox(b));
37504 if(south && south.isVisible()){
37505 var b = south.getBox();
37506 var m = south.getMargins();
37507 b.width = w - (m.left+m.right);
37509 var totalHeight = (b.height + m.top + m.bottom);
37510 b.y = h - totalHeight + m.top;
37511 centerH -= totalHeight;
37512 south.updateBox(this.safeBox(b));
37514 if(west && west.isVisible()){
37515 var b = west.getBox();
37516 var m = west.getMargins();
37517 b.height = centerH - (m.top+m.bottom);
37519 b.y = centerY + m.top;
37520 var totalWidth = (b.width + m.left + m.right);
37521 centerX += totalWidth;
37522 centerW -= totalWidth;
37523 west.updateBox(this.safeBox(b));
37525 if(east && east.isVisible()){
37526 var b = east.getBox();
37527 var m = east.getMargins();
37528 b.height = centerH - (m.top+m.bottom);
37529 var totalWidth = (b.width + m.left + m.right);
37530 b.x = w - totalWidth + m.left;
37531 b.y = centerY + m.top;
37532 centerW -= totalWidth;
37533 east.updateBox(this.safeBox(b));
37536 var m = center.getMargins();
37538 x: centerX + m.left,
37539 y: centerY + m.top,
37540 width: centerW - (m.left+m.right),
37541 height: centerH - (m.top+m.bottom)
37543 //if(this.hideOnLayout){
37544 //center.el.setStyle("display", "block");
37546 center.updateBox(this.safeBox(centerBox));
37549 this.fireEvent("layout", this);
37553 safeBox : function(box){
37554 box.width = Math.max(0, box.width);
37555 box.height = Math.max(0, box.height);
37560 * Adds a ContentPanel (or subclass) to this layout.
37561 * @param {String} target The target region key (north, south, east, west or center).
37562 * @param {Roo.ContentPanel} panel The panel to add
37563 * @return {Roo.ContentPanel} The added panel
37565 add : function(target, panel){
37567 target = target.toLowerCase();
37568 return this.regions[target].add(panel);
37572 * Remove a ContentPanel (or subclass) to this layout.
37573 * @param {String} target The target region key (north, south, east, west or center).
37574 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37575 * @return {Roo.ContentPanel} The removed panel
37577 remove : function(target, panel){
37578 target = target.toLowerCase();
37579 return this.regions[target].remove(panel);
37583 * Searches all regions for a panel with the specified id
37584 * @param {String} panelId
37585 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37587 findPanel : function(panelId){
37588 var rs = this.regions;
37589 for(var target in rs){
37590 if(typeof rs[target] != "function"){
37591 var p = rs[target].getPanel(panelId);
37601 * Searches all regions for a panel with the specified id and activates (shows) it.
37602 * @param {String/ContentPanel} panelId The panels id or the panel itself
37603 * @return {Roo.ContentPanel} The shown panel or null
37605 showPanel : function(panelId) {
37606 var rs = this.regions;
37607 for(var target in rs){
37608 var r = rs[target];
37609 if(typeof r != "function"){
37610 if(r.hasPanel(panelId)){
37611 return r.showPanel(panelId);
37619 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37620 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37623 restoreState : function(provider){
37625 provider = Roo.state.Manager;
37627 var sm = new Roo.LayoutStateManager();
37628 sm.init(this, provider);
37634 * Adds a xtype elements to the layout.
37638 xtype : 'ContentPanel',
37645 xtype : 'NestedLayoutPanel',
37651 items : [ ... list of content panels or nested layout panels.. ]
37655 * @param {Object} cfg Xtype definition of item to add.
37657 addxtype : function(cfg)
37659 // basically accepts a pannel...
37660 // can accept a layout region..!?!?
37661 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37664 // theory? children can only be panels??
37666 //if (!cfg.xtype.match(/Panel$/)) {
37671 if (typeof(cfg.region) == 'undefined') {
37672 Roo.log("Failed to add Panel, region was not set");
37676 var region = cfg.region;
37682 xitems = cfg.items;
37687 if ( region == 'center') {
37688 Roo.log("Center: " + cfg.title);
37694 case 'Content': // ContentPanel (el, cfg)
37695 case 'Scroll': // ContentPanel (el, cfg)
37697 cfg.autoCreate = cfg.autoCreate || true;
37698 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37700 // var el = this.el.createChild();
37701 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37704 this.add(region, ret);
37708 case 'TreePanel': // our new panel!
37709 cfg.el = this.el.createChild();
37710 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37711 this.add(region, ret);
37716 // create a new Layout (which is a Border Layout...
37718 var clayout = cfg.layout;
37719 clayout.el = this.el.createChild();
37720 clayout.items = clayout.items || [];
37724 // replace this exitems with the clayout ones..
37725 xitems = clayout.items;
37727 // force background off if it's in center...
37728 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37729 cfg.background = false;
37731 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37734 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37735 //console.log('adding nested layout panel ' + cfg.toSource());
37736 this.add(region, ret);
37737 nb = {}; /// find first...
37742 // needs grid and region
37744 //var el = this.getRegion(region).el.createChild();
37746 *var el = this.el.createChild();
37747 // create the grid first...
37748 cfg.grid.container = el;
37749 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37752 if (region == 'center' && this.active ) {
37753 cfg.background = false;
37756 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37758 this.add(region, ret);
37760 if (cfg.background) {
37761 // render grid on panel activation (if panel background)
37762 ret.on('activate', function(gp) {
37763 if (!gp.grid.rendered) {
37764 // gp.grid.render(el);
37768 // cfg.grid.render(el);
37774 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37775 // it was the old xcomponent building that caused this before.
37776 // espeically if border is the top element in the tree.
37786 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37788 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37789 this.add(region, ret);
37793 throw "Can not add '" + cfg.xtype + "' to Border";
37799 this.beginUpdate();
37803 Roo.each(xitems, function(i) {
37804 region = nb && i.region ? i.region : false;
37806 var add = ret.addxtype(i);
37809 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37810 if (!i.background) {
37811 abn[region] = nb[region] ;
37818 // make the last non-background panel active..
37819 //if (nb) { Roo.log(abn); }
37822 for(var r in abn) {
37823 region = this.getRegion(r);
37825 // tried using nb[r], but it does not work..
37827 region.showPanel(abn[r]);
37838 factory : function(cfg)
37841 var validRegions = Roo.bootstrap.layout.Border.regions;
37843 var target = cfg.region;
37846 var r = Roo.bootstrap.layout;
37850 return new r.North(cfg);
37852 return new r.South(cfg);
37854 return new r.East(cfg);
37856 return new r.West(cfg);
37858 return new r.Center(cfg);
37860 throw 'Layout region "'+target+'" not supported.';
37867 * Ext JS Library 1.1.1
37868 * Copyright(c) 2006-2007, Ext JS, LLC.
37870 * Originally Released Under LGPL - original licence link has changed is not relivant.
37873 * <script type="text/javascript">
37877 * @class Roo.bootstrap.layout.Basic
37878 * @extends Roo.util.Observable
37879 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37880 * and does not have a titlebar, tabs or any other features. All it does is size and position
37881 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37882 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37883 * @cfg {string} region the region that it inhabits..
37884 * @cfg {bool} skipConfig skip config?
37888 Roo.bootstrap.layout.Basic = function(config){
37890 this.mgr = config.mgr;
37892 this.position = config.region;
37894 var skipConfig = config.skipConfig;
37898 * @scope Roo.BasicLayoutRegion
37902 * @event beforeremove
37903 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37904 * @param {Roo.LayoutRegion} this
37905 * @param {Roo.ContentPanel} panel The panel
37906 * @param {Object} e The cancel event object
37908 "beforeremove" : true,
37910 * @event invalidated
37911 * Fires when the layout for this region is changed.
37912 * @param {Roo.LayoutRegion} this
37914 "invalidated" : true,
37916 * @event visibilitychange
37917 * Fires when this region is shown or hidden
37918 * @param {Roo.LayoutRegion} this
37919 * @param {Boolean} visibility true or false
37921 "visibilitychange" : true,
37923 * @event paneladded
37924 * Fires when a panel is added.
37925 * @param {Roo.LayoutRegion} this
37926 * @param {Roo.ContentPanel} panel The panel
37928 "paneladded" : true,
37930 * @event panelremoved
37931 * Fires when a panel is removed.
37932 * @param {Roo.LayoutRegion} this
37933 * @param {Roo.ContentPanel} panel The panel
37935 "panelremoved" : true,
37937 * @event beforecollapse
37938 * Fires when this region before collapse.
37939 * @param {Roo.LayoutRegion} this
37941 "beforecollapse" : true,
37944 * Fires when this region is collapsed.
37945 * @param {Roo.LayoutRegion} this
37947 "collapsed" : true,
37950 * Fires when this region is expanded.
37951 * @param {Roo.LayoutRegion} this
37956 * Fires when this region is slid into view.
37957 * @param {Roo.LayoutRegion} this
37959 "slideshow" : true,
37962 * Fires when this region slides out of view.
37963 * @param {Roo.LayoutRegion} this
37965 "slidehide" : true,
37967 * @event panelactivated
37968 * Fires when a panel is activated.
37969 * @param {Roo.LayoutRegion} this
37970 * @param {Roo.ContentPanel} panel The activated panel
37972 "panelactivated" : true,
37975 * Fires when the user resizes this region.
37976 * @param {Roo.LayoutRegion} this
37977 * @param {Number} newSize The new size (width for east/west, height for north/south)
37981 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37982 this.panels = new Roo.util.MixedCollection();
37983 this.panels.getKey = this.getPanelId.createDelegate(this);
37985 this.activePanel = null;
37986 // ensure listeners are added...
37988 if (config.listeners || config.events) {
37989 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37990 listeners : config.listeners || {},
37991 events : config.events || {}
37995 if(skipConfig !== true){
37996 this.applyConfig(config);
38000 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38002 getPanelId : function(p){
38006 applyConfig : function(config){
38007 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38008 this.config = config;
38013 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38014 * the width, for horizontal (north, south) the height.
38015 * @param {Number} newSize The new width or height
38017 resizeTo : function(newSize){
38018 var el = this.el ? this.el :
38019 (this.activePanel ? this.activePanel.getEl() : null);
38021 switch(this.position){
38024 el.setWidth(newSize);
38025 this.fireEvent("resized", this, newSize);
38029 el.setHeight(newSize);
38030 this.fireEvent("resized", this, newSize);
38036 getBox : function(){
38037 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38040 getMargins : function(){
38041 return this.margins;
38044 updateBox : function(box){
38046 var el = this.activePanel.getEl();
38047 el.dom.style.left = box.x + "px";
38048 el.dom.style.top = box.y + "px";
38049 this.activePanel.setSize(box.width, box.height);
38053 * Returns the container element for this region.
38054 * @return {Roo.Element}
38056 getEl : function(){
38057 return this.activePanel;
38061 * Returns true if this region is currently visible.
38062 * @return {Boolean}
38064 isVisible : function(){
38065 return this.activePanel ? true : false;
38068 setActivePanel : function(panel){
38069 panel = this.getPanel(panel);
38070 if(this.activePanel && this.activePanel != panel){
38071 this.activePanel.setActiveState(false);
38072 this.activePanel.getEl().setLeftTop(-10000,-10000);
38074 this.activePanel = panel;
38075 panel.setActiveState(true);
38077 panel.setSize(this.box.width, this.box.height);
38079 this.fireEvent("panelactivated", this, panel);
38080 this.fireEvent("invalidated");
38084 * Show the specified panel.
38085 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38086 * @return {Roo.ContentPanel} The shown panel or null
38088 showPanel : function(panel){
38089 panel = this.getPanel(panel);
38091 this.setActivePanel(panel);
38097 * Get the active panel for this region.
38098 * @return {Roo.ContentPanel} The active panel or null
38100 getActivePanel : function(){
38101 return this.activePanel;
38105 * Add the passed ContentPanel(s)
38106 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38107 * @return {Roo.ContentPanel} The panel added (if only one was added)
38109 add : function(panel){
38110 if(arguments.length > 1){
38111 for(var i = 0, len = arguments.length; i < len; i++) {
38112 this.add(arguments[i]);
38116 if(this.hasPanel(panel)){
38117 this.showPanel(panel);
38120 var el = panel.getEl();
38121 if(el.dom.parentNode != this.mgr.el.dom){
38122 this.mgr.el.dom.appendChild(el.dom);
38124 if(panel.setRegion){
38125 panel.setRegion(this);
38127 this.panels.add(panel);
38128 el.setStyle("position", "absolute");
38129 if(!panel.background){
38130 this.setActivePanel(panel);
38131 if(this.config.initialSize && this.panels.getCount()==1){
38132 this.resizeTo(this.config.initialSize);
38135 this.fireEvent("paneladded", this, panel);
38140 * Returns true if the panel is in this region.
38141 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38142 * @return {Boolean}
38144 hasPanel : function(panel){
38145 if(typeof panel == "object"){ // must be panel obj
38146 panel = panel.getId();
38148 return this.getPanel(panel) ? true : false;
38152 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38153 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38154 * @param {Boolean} preservePanel Overrides the config preservePanel option
38155 * @return {Roo.ContentPanel} The panel that was removed
38157 remove : function(panel, preservePanel){
38158 panel = this.getPanel(panel);
38163 this.fireEvent("beforeremove", this, panel, e);
38164 if(e.cancel === true){
38167 var panelId = panel.getId();
38168 this.panels.removeKey(panelId);
38173 * Returns the panel specified or null if it's not in this region.
38174 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38175 * @return {Roo.ContentPanel}
38177 getPanel : function(id){
38178 if(typeof id == "object"){ // must be panel obj
38181 return this.panels.get(id);
38185 * Returns this regions position (north/south/east/west/center).
38188 getPosition: function(){
38189 return this.position;
38193 * Ext JS Library 1.1.1
38194 * Copyright(c) 2006-2007, Ext JS, LLC.
38196 * Originally Released Under LGPL - original licence link has changed is not relivant.
38199 * <script type="text/javascript">
38203 * @class Roo.bootstrap.layout.Region
38204 * @extends Roo.bootstrap.layout.Basic
38205 * This class represents a region in a layout manager.
38207 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38208 * @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})
38209 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38210 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38211 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38212 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38213 * @cfg {String} title The title for the region (overrides panel titles)
38214 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38215 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38216 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38217 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38218 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38219 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38220 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38221 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38222 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38223 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38225 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38226 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38227 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38228 * @cfg {Number} width For East/West panels
38229 * @cfg {Number} height For North/South panels
38230 * @cfg {Boolean} split To show the splitter
38231 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38233 * @cfg {string} cls Extra CSS classes to add to region
38235 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38236 * @cfg {string} region the region that it inhabits..
38239 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38240 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38242 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38243 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38244 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38246 Roo.bootstrap.layout.Region = function(config)
38248 this.applyConfig(config);
38250 var mgr = config.mgr;
38251 var pos = config.region;
38252 config.skipConfig = true;
38253 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38256 this.onRender(mgr.el);
38259 this.visible = true;
38260 this.collapsed = false;
38261 this.unrendered_panels = [];
38264 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38266 position: '', // set by wrapper (eg. north/south etc..)
38267 unrendered_panels : null, // unrendered panels.
38269 tabPosition : false,
38271 mgr: false, // points to 'Border'
38274 createBody : function(){
38275 /** This region's body element
38276 * @type Roo.Element */
38277 this.bodyEl = this.el.createChild({
38279 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38283 onRender: function(ctr, pos)
38285 var dh = Roo.DomHelper;
38286 /** This region's container element
38287 * @type Roo.Element */
38288 this.el = dh.append(ctr.dom, {
38290 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38292 /** This region's title element
38293 * @type Roo.Element */
38295 this.titleEl = dh.append(this.el.dom, {
38297 unselectable: "on",
38298 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38300 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38301 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38305 this.titleEl.enableDisplayMode();
38306 /** This region's title text element
38307 * @type HTMLElement */
38308 this.titleTextEl = this.titleEl.dom.firstChild;
38309 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38311 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38312 this.closeBtn.enableDisplayMode();
38313 this.closeBtn.on("click", this.closeClicked, this);
38314 this.closeBtn.hide();
38316 this.createBody(this.config);
38317 if(this.config.hideWhenEmpty){
38319 this.on("paneladded", this.validateVisibility, this);
38320 this.on("panelremoved", this.validateVisibility, this);
38322 if(this.autoScroll){
38323 this.bodyEl.setStyle("overflow", "auto");
38325 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38327 //if(c.titlebar !== false){
38328 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38329 this.titleEl.hide();
38331 this.titleEl.show();
38332 if(this.config.title){
38333 this.titleTextEl.innerHTML = this.config.title;
38337 if(this.config.collapsed){
38338 this.collapse(true);
38340 if(this.config.hidden){
38344 if (this.unrendered_panels && this.unrendered_panels.length) {
38345 for (var i =0;i< this.unrendered_panels.length; i++) {
38346 this.add(this.unrendered_panels[i]);
38348 this.unrendered_panels = null;
38354 applyConfig : function(c)
38357 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38358 var dh = Roo.DomHelper;
38359 if(c.titlebar !== false){
38360 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38361 this.collapseBtn.on("click", this.collapse, this);
38362 this.collapseBtn.enableDisplayMode();
38364 if(c.showPin === true || this.showPin){
38365 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38366 this.stickBtn.enableDisplayMode();
38367 this.stickBtn.on("click", this.expand, this);
38368 this.stickBtn.hide();
38373 /** This region's collapsed element
38374 * @type Roo.Element */
38377 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38378 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38381 if(c.floatable !== false){
38382 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38383 this.collapsedEl.on("click", this.collapseClick, this);
38386 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38387 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38388 id: "message", unselectable: "on", style:{"float":"left"}});
38389 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38391 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38392 this.expandBtn.on("click", this.expand, this);
38396 if(this.collapseBtn){
38397 this.collapseBtn.setVisible(c.collapsible == true);
38400 this.cmargins = c.cmargins || this.cmargins ||
38401 (this.position == "west" || this.position == "east" ?
38402 {top: 0, left: 2, right:2, bottom: 0} :
38403 {top: 2, left: 0, right:0, bottom: 2});
38405 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38408 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38410 this.autoScroll = c.autoScroll || false;
38415 this.duration = c.duration || .30;
38416 this.slideDuration = c.slideDuration || .45;
38421 * Returns true if this region is currently visible.
38422 * @return {Boolean}
38424 isVisible : function(){
38425 return this.visible;
38429 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38430 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38432 //setCollapsedTitle : function(title){
38433 // title = title || " ";
38434 // if(this.collapsedTitleTextEl){
38435 // this.collapsedTitleTextEl.innerHTML = title;
38439 getBox : function(){
38441 // if(!this.collapsed){
38442 b = this.el.getBox(false, true);
38444 // b = this.collapsedEl.getBox(false, true);
38449 getMargins : function(){
38450 return this.margins;
38451 //return this.collapsed ? this.cmargins : this.margins;
38454 highlight : function(){
38455 this.el.addClass("x-layout-panel-dragover");
38458 unhighlight : function(){
38459 this.el.removeClass("x-layout-panel-dragover");
38462 updateBox : function(box)
38464 if (!this.bodyEl) {
38465 return; // not rendered yet..
38469 if(!this.collapsed){
38470 this.el.dom.style.left = box.x + "px";
38471 this.el.dom.style.top = box.y + "px";
38472 this.updateBody(box.width, box.height);
38474 this.collapsedEl.dom.style.left = box.x + "px";
38475 this.collapsedEl.dom.style.top = box.y + "px";
38476 this.collapsedEl.setSize(box.width, box.height);
38479 this.tabs.autoSizeTabs();
38483 updateBody : function(w, h)
38486 this.el.setWidth(w);
38487 w -= this.el.getBorderWidth("rl");
38488 if(this.config.adjustments){
38489 w += this.config.adjustments[0];
38492 if(h !== null && h > 0){
38493 this.el.setHeight(h);
38494 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38495 h -= this.el.getBorderWidth("tb");
38496 if(this.config.adjustments){
38497 h += this.config.adjustments[1];
38499 this.bodyEl.setHeight(h);
38501 h = this.tabs.syncHeight(h);
38504 if(this.panelSize){
38505 w = w !== null ? w : this.panelSize.width;
38506 h = h !== null ? h : this.panelSize.height;
38508 if(this.activePanel){
38509 var el = this.activePanel.getEl();
38510 w = w !== null ? w : el.getWidth();
38511 h = h !== null ? h : el.getHeight();
38512 this.panelSize = {width: w, height: h};
38513 this.activePanel.setSize(w, h);
38515 if(Roo.isIE && this.tabs){
38516 this.tabs.el.repaint();
38521 * Returns the container element for this region.
38522 * @return {Roo.Element}
38524 getEl : function(){
38529 * Hides this region.
38532 //if(!this.collapsed){
38533 this.el.dom.style.left = "-2000px";
38536 // this.collapsedEl.dom.style.left = "-2000px";
38537 // this.collapsedEl.hide();
38539 this.visible = false;
38540 this.fireEvent("visibilitychange", this, false);
38544 * Shows this region if it was previously hidden.
38547 //if(!this.collapsed){
38550 // this.collapsedEl.show();
38552 this.visible = true;
38553 this.fireEvent("visibilitychange", this, true);
38556 closeClicked : function(){
38557 if(this.activePanel){
38558 this.remove(this.activePanel);
38562 collapseClick : function(e){
38564 e.stopPropagation();
38567 e.stopPropagation();
38573 * Collapses this region.
38574 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38577 collapse : function(skipAnim, skipCheck = false){
38578 if(this.collapsed) {
38582 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38584 this.collapsed = true;
38586 this.split.el.hide();
38588 if(this.config.animate && skipAnim !== true){
38589 this.fireEvent("invalidated", this);
38590 this.animateCollapse();
38592 this.el.setLocation(-20000,-20000);
38594 this.collapsedEl.show();
38595 this.fireEvent("collapsed", this);
38596 this.fireEvent("invalidated", this);
38602 animateCollapse : function(){
38607 * Expands this region if it was previously collapsed.
38608 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38609 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38612 expand : function(e, skipAnim){
38614 e.stopPropagation();
38616 if(!this.collapsed || this.el.hasActiveFx()) {
38620 this.afterSlideIn();
38623 this.collapsed = false;
38624 if(this.config.animate && skipAnim !== true){
38625 this.animateExpand();
38629 this.split.el.show();
38631 this.collapsedEl.setLocation(-2000,-2000);
38632 this.collapsedEl.hide();
38633 this.fireEvent("invalidated", this);
38634 this.fireEvent("expanded", this);
38638 animateExpand : function(){
38642 initTabs : function()
38644 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38646 var ts = new Roo.bootstrap.panel.Tabs({
38647 el: this.bodyEl.dom,
38649 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38650 disableTooltips: this.config.disableTabTips,
38651 toolbar : this.config.toolbar
38654 if(this.config.hideTabs){
38655 ts.stripWrap.setDisplayed(false);
38658 ts.resizeTabs = this.config.resizeTabs === true;
38659 ts.minTabWidth = this.config.minTabWidth || 40;
38660 ts.maxTabWidth = this.config.maxTabWidth || 250;
38661 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38662 ts.monitorResize = false;
38663 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38664 ts.bodyEl.addClass('roo-layout-tabs-body');
38665 this.panels.each(this.initPanelAsTab, this);
38668 initPanelAsTab : function(panel){
38669 var ti = this.tabs.addTab(
38673 this.config.closeOnTab && panel.isClosable(),
38676 if(panel.tabTip !== undefined){
38677 ti.setTooltip(panel.tabTip);
38679 ti.on("activate", function(){
38680 this.setActivePanel(panel);
38683 if(this.config.closeOnTab){
38684 ti.on("beforeclose", function(t, e){
38686 this.remove(panel);
38690 panel.tabItem = ti;
38695 updatePanelTitle : function(panel, title)
38697 if(this.activePanel == panel){
38698 this.updateTitle(title);
38701 var ti = this.tabs.getTab(panel.getEl().id);
38703 if(panel.tabTip !== undefined){
38704 ti.setTooltip(panel.tabTip);
38709 updateTitle : function(title){
38710 if(this.titleTextEl && !this.config.title){
38711 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38715 setActivePanel : function(panel)
38717 panel = this.getPanel(panel);
38718 if(this.activePanel && this.activePanel != panel){
38719 if(this.activePanel.setActiveState(false) === false){
38723 this.activePanel = panel;
38724 panel.setActiveState(true);
38725 if(this.panelSize){
38726 panel.setSize(this.panelSize.width, this.panelSize.height);
38729 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38731 this.updateTitle(panel.getTitle());
38733 this.fireEvent("invalidated", this);
38735 this.fireEvent("panelactivated", this, panel);
38739 * Shows the specified panel.
38740 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38741 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38743 showPanel : function(panel)
38745 panel = this.getPanel(panel);
38748 var tab = this.tabs.getTab(panel.getEl().id);
38749 if(tab.isHidden()){
38750 this.tabs.unhideTab(tab.id);
38754 this.setActivePanel(panel);
38761 * Get the active panel for this region.
38762 * @return {Roo.ContentPanel} The active panel or null
38764 getActivePanel : function(){
38765 return this.activePanel;
38768 validateVisibility : function(){
38769 if(this.panels.getCount() < 1){
38770 this.updateTitle(" ");
38771 this.closeBtn.hide();
38774 if(!this.isVisible()){
38781 * Adds the passed ContentPanel(s) to this region.
38782 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38783 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38785 add : function(panel)
38787 if(arguments.length > 1){
38788 for(var i = 0, len = arguments.length; i < len; i++) {
38789 this.add(arguments[i]);
38794 // if we have not been rendered yet, then we can not really do much of this..
38795 if (!this.bodyEl) {
38796 this.unrendered_panels.push(panel);
38803 if(this.hasPanel(panel)){
38804 this.showPanel(panel);
38807 panel.setRegion(this);
38808 this.panels.add(panel);
38809 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38810 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38811 // and hide them... ???
38812 this.bodyEl.dom.appendChild(panel.getEl().dom);
38813 if(panel.background !== true){
38814 this.setActivePanel(panel);
38816 this.fireEvent("paneladded", this, panel);
38823 this.initPanelAsTab(panel);
38827 if(panel.background !== true){
38828 this.tabs.activate(panel.getEl().id);
38830 this.fireEvent("paneladded", this, panel);
38835 * Hides the tab for the specified panel.
38836 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38838 hidePanel : function(panel){
38839 if(this.tabs && (panel = this.getPanel(panel))){
38840 this.tabs.hideTab(panel.getEl().id);
38845 * Unhides the tab for a previously hidden panel.
38846 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38848 unhidePanel : function(panel){
38849 if(this.tabs && (panel = this.getPanel(panel))){
38850 this.tabs.unhideTab(panel.getEl().id);
38854 clearPanels : function(){
38855 while(this.panels.getCount() > 0){
38856 this.remove(this.panels.first());
38861 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38862 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38863 * @param {Boolean} preservePanel Overrides the config preservePanel option
38864 * @return {Roo.ContentPanel} The panel that was removed
38866 remove : function(panel, preservePanel)
38868 panel = this.getPanel(panel);
38873 this.fireEvent("beforeremove", this, panel, e);
38874 if(e.cancel === true){
38877 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38878 var panelId = panel.getId();
38879 this.panels.removeKey(panelId);
38881 document.body.appendChild(panel.getEl().dom);
38884 this.tabs.removeTab(panel.getEl().id);
38885 }else if (!preservePanel){
38886 this.bodyEl.dom.removeChild(panel.getEl().dom);
38888 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38889 var p = this.panels.first();
38890 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38891 tempEl.appendChild(p.getEl().dom);
38892 this.bodyEl.update("");
38893 this.bodyEl.dom.appendChild(p.getEl().dom);
38895 this.updateTitle(p.getTitle());
38897 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38898 this.setActivePanel(p);
38900 panel.setRegion(null);
38901 if(this.activePanel == panel){
38902 this.activePanel = null;
38904 if(this.config.autoDestroy !== false && preservePanel !== true){
38905 try{panel.destroy();}catch(e){}
38907 this.fireEvent("panelremoved", this, panel);
38912 * Returns the TabPanel component used by this region
38913 * @return {Roo.TabPanel}
38915 getTabs : function(){
38919 createTool : function(parentEl, className){
38920 var btn = Roo.DomHelper.append(parentEl, {
38922 cls: "x-layout-tools-button",
38925 cls: "roo-layout-tools-button-inner " + className,
38929 btn.addClassOnOver("roo-layout-tools-button-over");
38934 * Ext JS Library 1.1.1
38935 * Copyright(c) 2006-2007, Ext JS, LLC.
38937 * Originally Released Under LGPL - original licence link has changed is not relivant.
38940 * <script type="text/javascript">
38946 * @class Roo.SplitLayoutRegion
38947 * @extends Roo.LayoutRegion
38948 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38950 Roo.bootstrap.layout.Split = function(config){
38951 this.cursor = config.cursor;
38952 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38955 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38957 splitTip : "Drag to resize.",
38958 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38959 useSplitTips : false,
38961 applyConfig : function(config){
38962 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38965 onRender : function(ctr,pos) {
38967 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38968 if(!this.config.split){
38973 var splitEl = Roo.DomHelper.append(ctr.dom, {
38975 id: this.el.id + "-split",
38976 cls: "roo-layout-split roo-layout-split-"+this.position,
38979 /** The SplitBar for this region
38980 * @type Roo.SplitBar */
38981 // does not exist yet...
38982 Roo.log([this.position, this.orientation]);
38984 this.split = new Roo.bootstrap.SplitBar({
38985 dragElement : splitEl,
38986 resizingElement: this.el,
38987 orientation : this.orientation
38990 this.split.on("moved", this.onSplitMove, this);
38991 this.split.useShim = this.config.useShim === true;
38992 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38993 if(this.useSplitTips){
38994 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38996 //if(config.collapsible){
38997 // this.split.el.on("dblclick", this.collapse, this);
39000 if(typeof this.config.minSize != "undefined"){
39001 this.split.minSize = this.config.minSize;
39003 if(typeof this.config.maxSize != "undefined"){
39004 this.split.maxSize = this.config.maxSize;
39006 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39007 this.hideSplitter();
39012 getHMaxSize : function(){
39013 var cmax = this.config.maxSize || 10000;
39014 var center = this.mgr.getRegion("center");
39015 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39018 getVMaxSize : function(){
39019 var cmax = this.config.maxSize || 10000;
39020 var center = this.mgr.getRegion("center");
39021 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39024 onSplitMove : function(split, newSize){
39025 this.fireEvent("resized", this, newSize);
39029 * Returns the {@link Roo.SplitBar} for this region.
39030 * @return {Roo.SplitBar}
39032 getSplitBar : function(){
39037 this.hideSplitter();
39038 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39041 hideSplitter : function(){
39043 this.split.el.setLocation(-2000,-2000);
39044 this.split.el.hide();
39050 this.split.el.show();
39052 Roo.bootstrap.layout.Split.superclass.show.call(this);
39055 beforeSlide: function(){
39056 if(Roo.isGecko){// firefox overflow auto bug workaround
39057 this.bodyEl.clip();
39059 this.tabs.bodyEl.clip();
39061 if(this.activePanel){
39062 this.activePanel.getEl().clip();
39064 if(this.activePanel.beforeSlide){
39065 this.activePanel.beforeSlide();
39071 afterSlide : function(){
39072 if(Roo.isGecko){// firefox overflow auto bug workaround
39073 this.bodyEl.unclip();
39075 this.tabs.bodyEl.unclip();
39077 if(this.activePanel){
39078 this.activePanel.getEl().unclip();
39079 if(this.activePanel.afterSlide){
39080 this.activePanel.afterSlide();
39086 initAutoHide : function(){
39087 if(this.autoHide !== false){
39088 if(!this.autoHideHd){
39089 var st = new Roo.util.DelayedTask(this.slideIn, this);
39090 this.autoHideHd = {
39091 "mouseout": function(e){
39092 if(!e.within(this.el, true)){
39096 "mouseover" : function(e){
39102 this.el.on(this.autoHideHd);
39106 clearAutoHide : function(){
39107 if(this.autoHide !== false){
39108 this.el.un("mouseout", this.autoHideHd.mouseout);
39109 this.el.un("mouseover", this.autoHideHd.mouseover);
39113 clearMonitor : function(){
39114 Roo.get(document).un("click", this.slideInIf, this);
39117 // these names are backwards but not changed for compat
39118 slideOut : function(){
39119 if(this.isSlid || this.el.hasActiveFx()){
39122 this.isSlid = true;
39123 if(this.collapseBtn){
39124 this.collapseBtn.hide();
39126 this.closeBtnState = this.closeBtn.getStyle('display');
39127 this.closeBtn.hide();
39129 this.stickBtn.show();
39132 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39133 this.beforeSlide();
39134 this.el.setStyle("z-index", 10001);
39135 this.el.slideIn(this.getSlideAnchor(), {
39136 callback: function(){
39138 this.initAutoHide();
39139 Roo.get(document).on("click", this.slideInIf, this);
39140 this.fireEvent("slideshow", this);
39147 afterSlideIn : function(){
39148 this.clearAutoHide();
39149 this.isSlid = false;
39150 this.clearMonitor();
39151 this.el.setStyle("z-index", "");
39152 if(this.collapseBtn){
39153 this.collapseBtn.show();
39155 this.closeBtn.setStyle('display', this.closeBtnState);
39157 this.stickBtn.hide();
39159 this.fireEvent("slidehide", this);
39162 slideIn : function(cb){
39163 if(!this.isSlid || this.el.hasActiveFx()){
39167 this.isSlid = false;
39168 this.beforeSlide();
39169 this.el.slideOut(this.getSlideAnchor(), {
39170 callback: function(){
39171 this.el.setLeftTop(-10000, -10000);
39173 this.afterSlideIn();
39181 slideInIf : function(e){
39182 if(!e.within(this.el)){
39187 animateCollapse : function(){
39188 this.beforeSlide();
39189 this.el.setStyle("z-index", 20000);
39190 var anchor = this.getSlideAnchor();
39191 this.el.slideOut(anchor, {
39192 callback : function(){
39193 this.el.setStyle("z-index", "");
39194 this.collapsedEl.slideIn(anchor, {duration:.3});
39196 this.el.setLocation(-10000,-10000);
39198 this.fireEvent("collapsed", this);
39205 animateExpand : function(){
39206 this.beforeSlide();
39207 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39208 this.el.setStyle("z-index", 20000);
39209 this.collapsedEl.hide({
39212 this.el.slideIn(this.getSlideAnchor(), {
39213 callback : function(){
39214 this.el.setStyle("z-index", "");
39217 this.split.el.show();
39219 this.fireEvent("invalidated", this);
39220 this.fireEvent("expanded", this);
39248 getAnchor : function(){
39249 return this.anchors[this.position];
39252 getCollapseAnchor : function(){
39253 return this.canchors[this.position];
39256 getSlideAnchor : function(){
39257 return this.sanchors[this.position];
39260 getAlignAdj : function(){
39261 var cm = this.cmargins;
39262 switch(this.position){
39278 getExpandAdj : function(){
39279 var c = this.collapsedEl, cm = this.cmargins;
39280 switch(this.position){
39282 return [-(cm.right+c.getWidth()+cm.left), 0];
39285 return [cm.right+c.getWidth()+cm.left, 0];
39288 return [0, -(cm.top+cm.bottom+c.getHeight())];
39291 return [0, cm.top+cm.bottom+c.getHeight()];
39297 * Ext JS Library 1.1.1
39298 * Copyright(c) 2006-2007, Ext JS, LLC.
39300 * Originally Released Under LGPL - original licence link has changed is not relivant.
39303 * <script type="text/javascript">
39306 * These classes are private internal classes
39308 Roo.bootstrap.layout.Center = function(config){
39309 config.region = "center";
39310 Roo.bootstrap.layout.Region.call(this, config);
39311 this.visible = true;
39312 this.minWidth = config.minWidth || 20;
39313 this.minHeight = config.minHeight || 20;
39316 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39318 // center panel can't be hidden
39322 // center panel can't be hidden
39325 getMinWidth: function(){
39326 return this.minWidth;
39329 getMinHeight: function(){
39330 return this.minHeight;
39344 Roo.bootstrap.layout.North = function(config)
39346 config.region = 'north';
39347 config.cursor = 'n-resize';
39349 Roo.bootstrap.layout.Split.call(this, config);
39353 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39354 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39355 this.split.el.addClass("roo-layout-split-v");
39357 //var size = config.initialSize || config.height;
39358 //if(this.el && typeof size != "undefined"){
39359 // this.el.setHeight(size);
39362 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39364 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39367 onRender : function(ctr, pos)
39369 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39370 var size = this.config.initialSize || this.config.height;
39371 if(this.el && typeof size != "undefined"){
39372 this.el.setHeight(size);
39377 getBox : function(){
39378 if(this.collapsed){
39379 return this.collapsedEl.getBox();
39381 var box = this.el.getBox();
39383 box.height += this.split.el.getHeight();
39388 updateBox : function(box){
39389 if(this.split && !this.collapsed){
39390 box.height -= this.split.el.getHeight();
39391 this.split.el.setLeft(box.x);
39392 this.split.el.setTop(box.y+box.height);
39393 this.split.el.setWidth(box.width);
39395 if(this.collapsed){
39396 this.updateBody(box.width, null);
39398 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39406 Roo.bootstrap.layout.South = function(config){
39407 config.region = 'south';
39408 config.cursor = 's-resize';
39409 Roo.bootstrap.layout.Split.call(this, config);
39411 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39412 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39413 this.split.el.addClass("roo-layout-split-v");
39418 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39419 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39421 onRender : function(ctr, pos)
39423 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39424 var size = this.config.initialSize || this.config.height;
39425 if(this.el && typeof size != "undefined"){
39426 this.el.setHeight(size);
39431 getBox : function(){
39432 if(this.collapsed){
39433 return this.collapsedEl.getBox();
39435 var box = this.el.getBox();
39437 var sh = this.split.el.getHeight();
39444 updateBox : function(box){
39445 if(this.split && !this.collapsed){
39446 var sh = this.split.el.getHeight();
39449 this.split.el.setLeft(box.x);
39450 this.split.el.setTop(box.y-sh);
39451 this.split.el.setWidth(box.width);
39453 if(this.collapsed){
39454 this.updateBody(box.width, null);
39456 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39460 Roo.bootstrap.layout.East = function(config){
39461 config.region = "east";
39462 config.cursor = "e-resize";
39463 Roo.bootstrap.layout.Split.call(this, config);
39465 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39466 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39467 this.split.el.addClass("roo-layout-split-h");
39471 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39472 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39474 onRender : function(ctr, pos)
39476 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39477 var size = this.config.initialSize || this.config.width;
39478 if(this.el && typeof size != "undefined"){
39479 this.el.setWidth(size);
39484 getBox : function(){
39485 if(this.collapsed){
39486 return this.collapsedEl.getBox();
39488 var box = this.el.getBox();
39490 var sw = this.split.el.getWidth();
39497 updateBox : function(box){
39498 if(this.split && !this.collapsed){
39499 var sw = this.split.el.getWidth();
39501 this.split.el.setLeft(box.x);
39502 this.split.el.setTop(box.y);
39503 this.split.el.setHeight(box.height);
39506 if(this.collapsed){
39507 this.updateBody(null, box.height);
39509 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39513 Roo.bootstrap.layout.West = function(config){
39514 config.region = "west";
39515 config.cursor = "w-resize";
39517 Roo.bootstrap.layout.Split.call(this, config);
39519 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39520 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39521 this.split.el.addClass("roo-layout-split-h");
39525 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39526 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39528 onRender: function(ctr, pos)
39530 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39531 var size = this.config.initialSize || this.config.width;
39532 if(typeof size != "undefined"){
39533 this.el.setWidth(size);
39537 getBox : function(){
39538 if(this.collapsed){
39539 return this.collapsedEl.getBox();
39541 var box = this.el.getBox();
39542 if (box.width == 0) {
39543 box.width = this.config.width; // kludge?
39546 box.width += this.split.el.getWidth();
39551 updateBox : function(box){
39552 if(this.split && !this.collapsed){
39553 var sw = this.split.el.getWidth();
39555 this.split.el.setLeft(box.x+box.width);
39556 this.split.el.setTop(box.y);
39557 this.split.el.setHeight(box.height);
39559 if(this.collapsed){
39560 this.updateBody(null, box.height);
39562 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39564 });Roo.namespace("Roo.bootstrap.panel");/*
39566 * Ext JS Library 1.1.1
39567 * Copyright(c) 2006-2007, Ext JS, LLC.
39569 * Originally Released Under LGPL - original licence link has changed is not relivant.
39572 * <script type="text/javascript">
39575 * @class Roo.ContentPanel
39576 * @extends Roo.util.Observable
39577 * A basic ContentPanel element.
39578 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39579 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39580 * @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
39581 * @cfg {Boolean} closable True if the panel can be closed/removed
39582 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39583 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39584 * @cfg {Toolbar} toolbar A toolbar for this panel
39585 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39586 * @cfg {String} title The title for this panel
39587 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39588 * @cfg {String} url Calls {@link #setUrl} with this value
39589 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39590 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39591 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39592 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39593 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39594 * @cfg {Boolean} badges render the badges
39595 * @cfg {String} cls extra classes to use
39596 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39599 * Create a new ContentPanel.
39600 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39601 * @param {String/Object} config A string to set only the title or a config object
39602 * @param {String} content (optional) Set the HTML content for this panel
39603 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39605 Roo.bootstrap.panel.Content = function( config){
39607 this.tpl = config.tpl || false;
39609 var el = config.el;
39610 var content = config.content;
39612 if(config.autoCreate){ // xtype is available if this is called from factory
39615 this.el = Roo.get(el);
39616 if(!this.el && config && config.autoCreate){
39617 if(typeof config.autoCreate == "object"){
39618 if(!config.autoCreate.id){
39619 config.autoCreate.id = config.id||el;
39621 this.el = Roo.DomHelper.append(document.body,
39622 config.autoCreate, true);
39626 cls: (config.cls || '') +
39627 (config.background ? ' bg-' + config.background : '') +
39628 " roo-layout-inactive-content",
39631 if (config.iframe) {
39635 style : 'border: 0px',
39636 src : 'about:blank'
39642 elcfg.html = config.html;
39646 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39647 if (config.iframe) {
39648 this.iframeEl = this.el.select('iframe',true).first();
39653 this.closable = false;
39654 this.loaded = false;
39655 this.active = false;
39658 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39660 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39662 this.wrapEl = this.el; //this.el.wrap();
39664 if (config.toolbar.items) {
39665 ti = config.toolbar.items ;
39666 delete config.toolbar.items ;
39670 this.toolbar.render(this.wrapEl, 'before');
39671 for(var i =0;i < ti.length;i++) {
39672 // Roo.log(['add child', items[i]]);
39673 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39675 this.toolbar.items = nitems;
39676 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39677 delete config.toolbar;
39681 // xtype created footer. - not sure if will work as we normally have to render first..
39682 if (this.footer && !this.footer.el && this.footer.xtype) {
39683 if (!this.wrapEl) {
39684 this.wrapEl = this.el.wrap();
39687 this.footer.container = this.wrapEl.createChild();
39689 this.footer = Roo.factory(this.footer, Roo);
39694 if(typeof config == "string"){
39695 this.title = config;
39697 Roo.apply(this, config);
39701 this.resizeEl = Roo.get(this.resizeEl, true);
39703 this.resizeEl = this.el;
39705 // handle view.xtype
39713 * Fires when this panel is activated.
39714 * @param {Roo.ContentPanel} this
39718 * @event deactivate
39719 * Fires when this panel is activated.
39720 * @param {Roo.ContentPanel} this
39722 "deactivate" : true,
39726 * Fires when this panel is resized if fitToFrame is true.
39727 * @param {Roo.ContentPanel} this
39728 * @param {Number} width The width after any component adjustments
39729 * @param {Number} height The height after any component adjustments
39735 * Fires when this tab is created
39736 * @param {Roo.ContentPanel} this
39747 if(this.autoScroll && !this.iframe){
39748 this.resizeEl.setStyle("overflow", "auto");
39750 // fix randome scrolling
39751 //this.el.on('scroll', function() {
39752 // Roo.log('fix random scolling');
39753 // this.scrollTo('top',0);
39756 content = content || this.content;
39758 this.setContent(content);
39760 if(config && config.url){
39761 this.setUrl(this.url, this.params, this.loadOnce);
39766 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39768 if (this.view && typeof(this.view.xtype) != 'undefined') {
39769 this.view.el = this.el.appendChild(document.createElement("div"));
39770 this.view = Roo.factory(this.view);
39771 this.view.render && this.view.render(false, '');
39775 this.fireEvent('render', this);
39778 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39788 setRegion : function(region){
39789 this.region = region;
39790 this.setActiveClass(region && !this.background);
39794 setActiveClass: function(state)
39797 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39798 this.el.setStyle('position','relative');
39800 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39801 this.el.setStyle('position', 'absolute');
39806 * Returns the toolbar for this Panel if one was configured.
39807 * @return {Roo.Toolbar}
39809 getToolbar : function(){
39810 return this.toolbar;
39813 setActiveState : function(active)
39815 this.active = active;
39816 this.setActiveClass(active);
39818 if(this.fireEvent("deactivate", this) === false){
39823 this.fireEvent("activate", this);
39827 * Updates this panel's element (not for iframe)
39828 * @param {String} content The new content
39829 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39831 setContent : function(content, loadScripts){
39836 this.el.update(content, loadScripts);
39839 ignoreResize : function(w, h){
39840 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39843 this.lastSize = {width: w, height: h};
39848 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39849 * @return {Roo.UpdateManager} The UpdateManager
39851 getUpdateManager : function(){
39855 return this.el.getUpdateManager();
39858 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39859 * Does not work with IFRAME contents
39860 * @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:
39863 url: "your-url.php",
39864 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39865 callback: yourFunction,
39866 scope: yourObject, //(optional scope)
39869 text: "Loading...",
39875 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39876 * 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.
39877 * @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}
39878 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39879 * @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.
39880 * @return {Roo.ContentPanel} this
39888 var um = this.el.getUpdateManager();
39889 um.update.apply(um, arguments);
39895 * 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.
39896 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39897 * @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)
39898 * @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)
39899 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39901 setUrl : function(url, params, loadOnce){
39903 this.iframeEl.dom.src = url;
39907 if(this.refreshDelegate){
39908 this.removeListener("activate", this.refreshDelegate);
39910 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39911 this.on("activate", this.refreshDelegate);
39912 return this.el.getUpdateManager();
39915 _handleRefresh : function(url, params, loadOnce){
39916 if(!loadOnce || !this.loaded){
39917 var updater = this.el.getUpdateManager();
39918 updater.update(url, params, this._setLoaded.createDelegate(this));
39922 _setLoaded : function(){
39923 this.loaded = true;
39927 * Returns this panel's id
39930 getId : function(){
39935 * Returns this panel's element - used by regiosn to add.
39936 * @return {Roo.Element}
39938 getEl : function(){
39939 return this.wrapEl || this.el;
39944 adjustForComponents : function(width, height)
39946 //Roo.log('adjustForComponents ');
39947 if(this.resizeEl != this.el){
39948 width -= this.el.getFrameWidth('lr');
39949 height -= this.el.getFrameWidth('tb');
39952 var te = this.toolbar.getEl();
39953 te.setWidth(width);
39954 height -= te.getHeight();
39957 var te = this.footer.getEl();
39958 te.setWidth(width);
39959 height -= te.getHeight();
39963 if(this.adjustments){
39964 width += this.adjustments[0];
39965 height += this.adjustments[1];
39967 return {"width": width, "height": height};
39970 setSize : function(width, height){
39971 if(this.fitToFrame && !this.ignoreResize(width, height)){
39972 if(this.fitContainer && this.resizeEl != this.el){
39973 this.el.setSize(width, height);
39975 var size = this.adjustForComponents(width, height);
39977 this.iframeEl.setSize(width,height);
39980 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39981 this.fireEvent('resize', this, size.width, size.height);
39988 * Returns this panel's title
39991 getTitle : function(){
39993 if (typeof(this.title) != 'object') {
39998 for (var k in this.title) {
39999 if (!this.title.hasOwnProperty(k)) {
40003 if (k.indexOf('-') >= 0) {
40004 var s = k.split('-');
40005 for (var i = 0; i<s.length; i++) {
40006 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40009 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40016 * Set this panel's title
40017 * @param {String} title
40019 setTitle : function(title){
40020 this.title = title;
40022 this.region.updatePanelTitle(this, title);
40027 * Returns true is this panel was configured to be closable
40028 * @return {Boolean}
40030 isClosable : function(){
40031 return this.closable;
40034 beforeSlide : function(){
40036 this.resizeEl.clip();
40039 afterSlide : function(){
40041 this.resizeEl.unclip();
40045 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40046 * Will fail silently if the {@link #setUrl} method has not been called.
40047 * This does not activate the panel, just updates its content.
40049 refresh : function(){
40050 if(this.refreshDelegate){
40051 this.loaded = false;
40052 this.refreshDelegate();
40057 * Destroys this panel
40059 destroy : function(){
40060 this.el.removeAllListeners();
40061 var tempEl = document.createElement("span");
40062 tempEl.appendChild(this.el.dom);
40063 tempEl.innerHTML = "";
40069 * form - if the content panel contains a form - this is a reference to it.
40070 * @type {Roo.form.Form}
40074 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40075 * This contains a reference to it.
40081 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40091 * @param {Object} cfg Xtype definition of item to add.
40095 getChildContainer: function () {
40096 return this.getEl();
40101 var ret = new Roo.factory(cfg);
40106 if (cfg.xtype.match(/^Form$/)) {
40109 //if (this.footer) {
40110 // el = this.footer.container.insertSibling(false, 'before');
40112 el = this.el.createChild();
40115 this.form = new Roo.form.Form(cfg);
40118 if ( this.form.allItems.length) {
40119 this.form.render(el.dom);
40123 // should only have one of theses..
40124 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40125 // views.. should not be just added - used named prop 'view''
40127 cfg.el = this.el.appendChild(document.createElement("div"));
40130 var ret = new Roo.factory(cfg);
40132 ret.render && ret.render(false, ''); // render blank..
40142 * @class Roo.bootstrap.panel.Grid
40143 * @extends Roo.bootstrap.panel.Content
40145 * Create a new GridPanel.
40146 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40147 * @param {Object} config A the config object
40153 Roo.bootstrap.panel.Grid = function(config)
40157 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40158 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40160 config.el = this.wrapper;
40161 //this.el = this.wrapper;
40163 if (config.container) {
40164 // ctor'ed from a Border/panel.grid
40167 this.wrapper.setStyle("overflow", "hidden");
40168 this.wrapper.addClass('roo-grid-container');
40173 if(config.toolbar){
40174 var tool_el = this.wrapper.createChild();
40175 this.toolbar = Roo.factory(config.toolbar);
40177 if (config.toolbar.items) {
40178 ti = config.toolbar.items ;
40179 delete config.toolbar.items ;
40183 this.toolbar.render(tool_el);
40184 for(var i =0;i < ti.length;i++) {
40185 // Roo.log(['add child', items[i]]);
40186 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40188 this.toolbar.items = nitems;
40190 delete config.toolbar;
40193 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40194 config.grid.scrollBody = true;;
40195 config.grid.monitorWindowResize = false; // turn off autosizing
40196 config.grid.autoHeight = false;
40197 config.grid.autoWidth = false;
40199 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40201 if (config.background) {
40202 // render grid on panel activation (if panel background)
40203 this.on('activate', function(gp) {
40204 if (!gp.grid.rendered) {
40205 gp.grid.render(this.wrapper);
40206 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40211 this.grid.render(this.wrapper);
40212 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40215 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40216 // ??? needed ??? config.el = this.wrapper;
40221 // xtype created footer. - not sure if will work as we normally have to render first..
40222 if (this.footer && !this.footer.el && this.footer.xtype) {
40224 var ctr = this.grid.getView().getFooterPanel(true);
40225 this.footer.dataSource = this.grid.dataSource;
40226 this.footer = Roo.factory(this.footer, Roo);
40227 this.footer.render(ctr);
40237 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40238 getId : function(){
40239 return this.grid.id;
40243 * Returns the grid for this panel
40244 * @return {Roo.bootstrap.Table}
40246 getGrid : function(){
40250 setSize : function(width, height){
40251 if(!this.ignoreResize(width, height)){
40252 var grid = this.grid;
40253 var size = this.adjustForComponents(width, height);
40254 // tfoot is not a footer?
40257 var gridel = grid.getGridEl();
40258 gridel.setSize(size.width, size.height);
40260 var tbd = grid.getGridEl().select('tbody', true).first();
40261 var thd = grid.getGridEl().select('thead',true).first();
40262 var tbf= grid.getGridEl().select('tfoot', true).first();
40265 size.height -= tbf.getHeight();
40268 size.height -= thd.getHeight();
40271 tbd.setSize(size.width, size.height );
40272 // this is for the account management tab -seems to work there.
40273 var thd = grid.getGridEl().select('thead',true).first();
40275 // tbd.setSize(size.width, size.height - thd.getHeight());
40284 beforeSlide : function(){
40285 this.grid.getView().scroller.clip();
40288 afterSlide : function(){
40289 this.grid.getView().scroller.unclip();
40292 destroy : function(){
40293 this.grid.destroy();
40295 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40300 * @class Roo.bootstrap.panel.Nest
40301 * @extends Roo.bootstrap.panel.Content
40303 * Create a new Panel, that can contain a layout.Border.
40306 * @param {Roo.BorderLayout} layout The layout for this panel
40307 * @param {String/Object} config A string to set only the title or a config object
40309 Roo.bootstrap.panel.Nest = function(config)
40311 // construct with only one argument..
40312 /* FIXME - implement nicer consturctors
40313 if (layout.layout) {
40315 layout = config.layout;
40316 delete config.layout;
40318 if (layout.xtype && !layout.getEl) {
40319 // then layout needs constructing..
40320 layout = Roo.factory(layout, Roo);
40324 config.el = config.layout.getEl();
40326 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40328 config.layout.monitorWindowResize = false; // turn off autosizing
40329 this.layout = config.layout;
40330 this.layout.getEl().addClass("roo-layout-nested-layout");
40331 this.layout.parent = this;
40338 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40340 setSize : function(width, height){
40341 if(!this.ignoreResize(width, height)){
40342 var size = this.adjustForComponents(width, height);
40343 var el = this.layout.getEl();
40344 if (size.height < 1) {
40345 el.setWidth(size.width);
40347 el.setSize(size.width, size.height);
40349 var touch = el.dom.offsetWidth;
40350 this.layout.layout();
40351 // ie requires a double layout on the first pass
40352 if(Roo.isIE && !this.initialized){
40353 this.initialized = true;
40354 this.layout.layout();
40359 // activate all subpanels if not currently active..
40361 setActiveState : function(active){
40362 this.active = active;
40363 this.setActiveClass(active);
40366 this.fireEvent("deactivate", this);
40370 this.fireEvent("activate", this);
40371 // not sure if this should happen before or after..
40372 if (!this.layout) {
40373 return; // should not happen..
40376 for (var r in this.layout.regions) {
40377 reg = this.layout.getRegion(r);
40378 if (reg.getActivePanel()) {
40379 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40380 reg.setActivePanel(reg.getActivePanel());
40383 if (!reg.panels.length) {
40386 reg.showPanel(reg.getPanel(0));
40395 * Returns the nested BorderLayout for this panel
40396 * @return {Roo.BorderLayout}
40398 getLayout : function(){
40399 return this.layout;
40403 * Adds a xtype elements to the layout of the nested panel
40407 xtype : 'ContentPanel',
40414 xtype : 'NestedLayoutPanel',
40420 items : [ ... list of content panels or nested layout panels.. ]
40424 * @param {Object} cfg Xtype definition of item to add.
40426 addxtype : function(cfg) {
40427 return this.layout.addxtype(cfg);
40432 * Ext JS Library 1.1.1
40433 * Copyright(c) 2006-2007, Ext JS, LLC.
40435 * Originally Released Under LGPL - original licence link has changed is not relivant.
40438 * <script type="text/javascript">
40441 * @class Roo.TabPanel
40442 * @extends Roo.util.Observable
40443 * A lightweight tab container.
40447 // basic tabs 1, built from existing content
40448 var tabs = new Roo.TabPanel("tabs1");
40449 tabs.addTab("script", "View Script");
40450 tabs.addTab("markup", "View Markup");
40451 tabs.activate("script");
40453 // more advanced tabs, built from javascript
40454 var jtabs = new Roo.TabPanel("jtabs");
40455 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40457 // set up the UpdateManager
40458 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40459 var updater = tab2.getUpdateManager();
40460 updater.setDefaultUrl("ajax1.htm");
40461 tab2.on('activate', updater.refresh, updater, true);
40463 // Use setUrl for Ajax loading
40464 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40465 tab3.setUrl("ajax2.htm", null, true);
40468 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40471 jtabs.activate("jtabs-1");
40474 * Create a new TabPanel.
40475 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40476 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40478 Roo.bootstrap.panel.Tabs = function(config){
40480 * The container element for this TabPanel.
40481 * @type Roo.Element
40483 this.el = Roo.get(config.el);
40486 if(typeof config == "boolean"){
40487 this.tabPosition = config ? "bottom" : "top";
40489 Roo.apply(this, config);
40493 if(this.tabPosition == "bottom"){
40494 // if tabs are at the bottom = create the body first.
40495 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40496 this.el.addClass("roo-tabs-bottom");
40498 // next create the tabs holders
40500 if (this.tabPosition == "west"){
40502 var reg = this.region; // fake it..
40504 if (!reg.mgr.parent) {
40507 reg = reg.mgr.parent.region;
40509 Roo.log("got nest?");
40511 if (reg.mgr.getRegion('west')) {
40512 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40513 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40514 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40515 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40516 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40524 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40525 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40526 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40527 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40532 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40535 // finally - if tabs are at the top, then create the body last..
40536 if(this.tabPosition != "bottom"){
40537 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40538 * @type Roo.Element
40540 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40541 this.el.addClass("roo-tabs-top");
40545 this.bodyEl.setStyle("position", "relative");
40547 this.active = null;
40548 this.activateDelegate = this.activate.createDelegate(this);
40553 * Fires when the active tab changes
40554 * @param {Roo.TabPanel} this
40555 * @param {Roo.TabPanelItem} activePanel The new active tab
40559 * @event beforetabchange
40560 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40561 * @param {Roo.TabPanel} this
40562 * @param {Object} e Set cancel to true on this object to cancel the tab change
40563 * @param {Roo.TabPanelItem} tab The tab being changed to
40565 "beforetabchange" : true
40568 Roo.EventManager.onWindowResize(this.onResize, this);
40569 this.cpad = this.el.getPadding("lr");
40570 this.hiddenCount = 0;
40573 // toolbar on the tabbar support...
40574 if (this.toolbar) {
40575 alert("no toolbar support yet");
40576 this.toolbar = false;
40578 var tcfg = this.toolbar;
40579 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40580 this.toolbar = new Roo.Toolbar(tcfg);
40581 if (Roo.isSafari) {
40582 var tbl = tcfg.container.child('table', true);
40583 tbl.setAttribute('width', '100%');
40591 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40594 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40596 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40598 tabPosition : "top",
40600 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40602 currentTabWidth : 0,
40604 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40608 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40612 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40614 preferredTabWidth : 175,
40616 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40618 resizeTabs : false,
40620 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40622 monitorResize : true,
40624 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40626 toolbar : false, // set by caller..
40628 region : false, /// set by caller
40630 disableTooltips : true, // not used yet...
40633 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40634 * @param {String} id The id of the div to use <b>or create</b>
40635 * @param {String} text The text for the tab
40636 * @param {String} content (optional) Content to put in the TabPanelItem body
40637 * @param {Boolean} closable (optional) True to create a close icon on the tab
40638 * @return {Roo.TabPanelItem} The created TabPanelItem
40640 addTab : function(id, text, content, closable, tpl)
40642 var item = new Roo.bootstrap.panel.TabItem({
40646 closable : closable,
40649 this.addTabItem(item);
40651 item.setContent(content);
40657 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40658 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40659 * @return {Roo.TabPanelItem}
40661 getTab : function(id){
40662 return this.items[id];
40666 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40667 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40669 hideTab : function(id){
40670 var t = this.items[id];
40673 this.hiddenCount++;
40674 this.autoSizeTabs();
40679 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40680 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40682 unhideTab : function(id){
40683 var t = this.items[id];
40685 t.setHidden(false);
40686 this.hiddenCount--;
40687 this.autoSizeTabs();
40692 * Adds an existing {@link Roo.TabPanelItem}.
40693 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40695 addTabItem : function(item)
40697 this.items[item.id] = item;
40698 this.items.push(item);
40699 this.autoSizeTabs();
40700 // if(this.resizeTabs){
40701 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40702 // this.autoSizeTabs();
40704 // item.autoSize();
40709 * Removes a {@link Roo.TabPanelItem}.
40710 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40712 removeTab : function(id){
40713 var items = this.items;
40714 var tab = items[id];
40715 if(!tab) { return; }
40716 var index = items.indexOf(tab);
40717 if(this.active == tab && items.length > 1){
40718 var newTab = this.getNextAvailable(index);
40723 this.stripEl.dom.removeChild(tab.pnode.dom);
40724 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40725 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40727 items.splice(index, 1);
40728 delete this.items[tab.id];
40729 tab.fireEvent("close", tab);
40730 tab.purgeListeners();
40731 this.autoSizeTabs();
40734 getNextAvailable : function(start){
40735 var items = this.items;
40737 // look for a next tab that will slide over to
40738 // replace the one being removed
40739 while(index < items.length){
40740 var item = items[++index];
40741 if(item && !item.isHidden()){
40745 // if one isn't found select the previous tab (on the left)
40748 var item = items[--index];
40749 if(item && !item.isHidden()){
40757 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40758 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40760 disableTab : function(id){
40761 var tab = this.items[id];
40762 if(tab && this.active != tab){
40768 * Enables a {@link Roo.TabPanelItem} that is disabled.
40769 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40771 enableTab : function(id){
40772 var tab = this.items[id];
40777 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40778 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40779 * @return {Roo.TabPanelItem} The TabPanelItem.
40781 activate : function(id)
40783 //Roo.log('activite:' + id);
40785 var tab = this.items[id];
40789 if(tab == this.active || tab.disabled){
40793 this.fireEvent("beforetabchange", this, e, tab);
40794 if(e.cancel !== true && !tab.disabled){
40796 this.active.hide();
40798 this.active = this.items[id];
40799 this.active.show();
40800 this.fireEvent("tabchange", this, this.active);
40806 * Gets the active {@link Roo.TabPanelItem}.
40807 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40809 getActiveTab : function(){
40810 return this.active;
40814 * Updates the tab body element to fit the height of the container element
40815 * for overflow scrolling
40816 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40818 syncHeight : function(targetHeight){
40819 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40820 var bm = this.bodyEl.getMargins();
40821 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40822 this.bodyEl.setHeight(newHeight);
40826 onResize : function(){
40827 if(this.monitorResize){
40828 this.autoSizeTabs();
40833 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40835 beginUpdate : function(){
40836 this.updating = true;
40840 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40842 endUpdate : function(){
40843 this.updating = false;
40844 this.autoSizeTabs();
40848 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40850 autoSizeTabs : function()
40852 var count = this.items.length;
40853 var vcount = count - this.hiddenCount;
40856 this.stripEl.hide();
40858 this.stripEl.show();
40861 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40866 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40867 var availWidth = Math.floor(w / vcount);
40868 var b = this.stripBody;
40869 if(b.getWidth() > w){
40870 var tabs = this.items;
40871 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40872 if(availWidth < this.minTabWidth){
40873 /*if(!this.sleft){ // incomplete scrolling code
40874 this.createScrollButtons();
40877 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40880 if(this.currentTabWidth < this.preferredTabWidth){
40881 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40887 * Returns the number of tabs in this TabPanel.
40890 getCount : function(){
40891 return this.items.length;
40895 * Resizes all the tabs to the passed width
40896 * @param {Number} The new width
40898 setTabWidth : function(width){
40899 this.currentTabWidth = width;
40900 for(var i = 0, len = this.items.length; i < len; i++) {
40901 if(!this.items[i].isHidden()) {
40902 this.items[i].setWidth(width);
40908 * Destroys this TabPanel
40909 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40911 destroy : function(removeEl){
40912 Roo.EventManager.removeResizeListener(this.onResize, this);
40913 for(var i = 0, len = this.items.length; i < len; i++){
40914 this.items[i].purgeListeners();
40916 if(removeEl === true){
40917 this.el.update("");
40922 createStrip : function(container)
40924 var strip = document.createElement("nav");
40925 strip.className = Roo.bootstrap.version == 4 ?
40926 "navbar-light bg-light" :
40927 "navbar navbar-default"; //"x-tabs-wrap";
40928 container.appendChild(strip);
40932 createStripList : function(strip)
40934 // div wrapper for retard IE
40935 // returns the "tr" element.
40936 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40937 //'<div class="x-tabs-strip-wrap">'+
40938 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40939 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40940 return strip.firstChild; //.firstChild.firstChild.firstChild;
40942 createBody : function(container)
40944 var body = document.createElement("div");
40945 Roo.id(body, "tab-body");
40946 //Roo.fly(body).addClass("x-tabs-body");
40947 Roo.fly(body).addClass("tab-content");
40948 container.appendChild(body);
40951 createItemBody :function(bodyEl, id){
40952 var body = Roo.getDom(id);
40954 body = document.createElement("div");
40957 //Roo.fly(body).addClass("x-tabs-item-body");
40958 Roo.fly(body).addClass("tab-pane");
40959 bodyEl.insertBefore(body, bodyEl.firstChild);
40963 createStripElements : function(stripEl, text, closable, tpl)
40965 var td = document.createElement("li"); // was td..
40966 td.className = 'nav-item';
40968 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40971 stripEl.appendChild(td);
40973 td.className = "x-tabs-closable";
40974 if(!this.closeTpl){
40975 this.closeTpl = new Roo.Template(
40976 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40977 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40978 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40981 var el = this.closeTpl.overwrite(td, {"text": text});
40982 var close = el.getElementsByTagName("div")[0];
40983 var inner = el.getElementsByTagName("em")[0];
40984 return {"el": el, "close": close, "inner": inner};
40987 // not sure what this is..
40988 // if(!this.tabTpl){
40989 //this.tabTpl = new Roo.Template(
40990 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40991 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40993 // this.tabTpl = new Roo.Template(
40994 // '<a href="#">' +
40995 // '<span unselectable="on"' +
40996 // (this.disableTooltips ? '' : ' title="{text}"') +
40997 // ' >{text}</span></a>'
41003 var template = tpl || this.tabTpl || false;
41006 template = new Roo.Template(
41007 Roo.bootstrap.version == 4 ?
41009 '<a class="nav-link" href="#" unselectable="on"' +
41010 (this.disableTooltips ? '' : ' title="{text}"') +
41013 '<a class="nav-link" href="#">' +
41014 '<span unselectable="on"' +
41015 (this.disableTooltips ? '' : ' title="{text}"') +
41016 ' >{text}</span></a>'
41021 switch (typeof(template)) {
41025 template = new Roo.Template(template);
41031 var el = template.overwrite(td, {"text": text});
41033 var inner = el.getElementsByTagName("span")[0];
41035 return {"el": el, "inner": inner};
41043 * @class Roo.TabPanelItem
41044 * @extends Roo.util.Observable
41045 * Represents an individual item (tab plus body) in a TabPanel.
41046 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41047 * @param {String} id The id of this TabPanelItem
41048 * @param {String} text The text for the tab of this TabPanelItem
41049 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41051 Roo.bootstrap.panel.TabItem = function(config){
41053 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41054 * @type Roo.TabPanel
41056 this.tabPanel = config.panel;
41058 * The id for this TabPanelItem
41061 this.id = config.id;
41063 this.disabled = false;
41065 this.text = config.text;
41067 this.loaded = false;
41068 this.closable = config.closable;
41071 * The body element for this TabPanelItem.
41072 * @type Roo.Element
41074 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41075 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41076 this.bodyEl.setStyle("display", "block");
41077 this.bodyEl.setStyle("zoom", "1");
41078 //this.hideAction();
41080 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41082 this.el = Roo.get(els.el);
41083 this.inner = Roo.get(els.inner, true);
41084 this.textEl = Roo.bootstrap.version == 4 ?
41085 this.el : Roo.get(this.el.dom.firstChild, true);
41087 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41088 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41091 // this.el.on("mousedown", this.onTabMouseDown, this);
41092 this.el.on("click", this.onTabClick, this);
41094 if(config.closable){
41095 var c = Roo.get(els.close, true);
41096 c.dom.title = this.closeText;
41097 c.addClassOnOver("close-over");
41098 c.on("click", this.closeClick, this);
41104 * Fires when this tab becomes the active tab.
41105 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41106 * @param {Roo.TabPanelItem} this
41110 * @event beforeclose
41111 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41112 * @param {Roo.TabPanelItem} this
41113 * @param {Object} e Set cancel to true on this object to cancel the close.
41115 "beforeclose": true,
41118 * Fires when this tab is closed.
41119 * @param {Roo.TabPanelItem} this
41123 * @event deactivate
41124 * Fires when this tab is no longer the active tab.
41125 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41126 * @param {Roo.TabPanelItem} this
41128 "deactivate" : true
41130 this.hidden = false;
41132 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41135 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41137 purgeListeners : function(){
41138 Roo.util.Observable.prototype.purgeListeners.call(this);
41139 this.el.removeAllListeners();
41142 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41145 this.status_node.addClass("active");
41148 this.tabPanel.stripWrap.repaint();
41150 this.fireEvent("activate", this.tabPanel, this);
41154 * Returns true if this tab is the active tab.
41155 * @return {Boolean}
41157 isActive : function(){
41158 return this.tabPanel.getActiveTab() == this;
41162 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41165 this.status_node.removeClass("active");
41167 this.fireEvent("deactivate", this.tabPanel, this);
41170 hideAction : function(){
41171 this.bodyEl.hide();
41172 this.bodyEl.setStyle("position", "absolute");
41173 this.bodyEl.setLeft("-20000px");
41174 this.bodyEl.setTop("-20000px");
41177 showAction : function(){
41178 this.bodyEl.setStyle("position", "relative");
41179 this.bodyEl.setTop("");
41180 this.bodyEl.setLeft("");
41181 this.bodyEl.show();
41185 * Set the tooltip for the tab.
41186 * @param {String} tooltip The tab's tooltip
41188 setTooltip : function(text){
41189 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41190 this.textEl.dom.qtip = text;
41191 this.textEl.dom.removeAttribute('title');
41193 this.textEl.dom.title = text;
41197 onTabClick : function(e){
41198 e.preventDefault();
41199 this.tabPanel.activate(this.id);
41202 onTabMouseDown : function(e){
41203 e.preventDefault();
41204 this.tabPanel.activate(this.id);
41207 getWidth : function(){
41208 return this.inner.getWidth();
41211 setWidth : function(width){
41212 var iwidth = width - this.linode.getPadding("lr");
41213 this.inner.setWidth(iwidth);
41214 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41215 this.linode.setWidth(width);
41219 * Show or hide the tab
41220 * @param {Boolean} hidden True to hide or false to show.
41222 setHidden : function(hidden){
41223 this.hidden = hidden;
41224 this.linode.setStyle("display", hidden ? "none" : "");
41228 * Returns true if this tab is "hidden"
41229 * @return {Boolean}
41231 isHidden : function(){
41232 return this.hidden;
41236 * Returns the text for this tab
41239 getText : function(){
41243 autoSize : function(){
41244 //this.el.beginMeasure();
41245 this.textEl.setWidth(1);
41247 * #2804 [new] Tabs in Roojs
41248 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41250 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41251 //this.el.endMeasure();
41255 * Sets the text for the tab (Note: this also sets the tooltip text)
41256 * @param {String} text The tab's text and tooltip
41258 setText : function(text){
41260 this.textEl.update(text);
41261 this.setTooltip(text);
41262 //if(!this.tabPanel.resizeTabs){
41263 // this.autoSize();
41267 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41269 activate : function(){
41270 this.tabPanel.activate(this.id);
41274 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41276 disable : function(){
41277 if(this.tabPanel.active != this){
41278 this.disabled = true;
41279 this.status_node.addClass("disabled");
41284 * Enables this TabPanelItem if it was previously disabled.
41286 enable : function(){
41287 this.disabled = false;
41288 this.status_node.removeClass("disabled");
41292 * Sets the content for this TabPanelItem.
41293 * @param {String} content The content
41294 * @param {Boolean} loadScripts true to look for and load scripts
41296 setContent : function(content, loadScripts){
41297 this.bodyEl.update(content, loadScripts);
41301 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41302 * @return {Roo.UpdateManager} The UpdateManager
41304 getUpdateManager : function(){
41305 return this.bodyEl.getUpdateManager();
41309 * Set a URL to be used to load the content for this TabPanelItem.
41310 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41311 * @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)
41312 * @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)
41313 * @return {Roo.UpdateManager} The UpdateManager
41315 setUrl : function(url, params, loadOnce){
41316 if(this.refreshDelegate){
41317 this.un('activate', this.refreshDelegate);
41319 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41320 this.on("activate", this.refreshDelegate);
41321 return this.bodyEl.getUpdateManager();
41325 _handleRefresh : function(url, params, loadOnce){
41326 if(!loadOnce || !this.loaded){
41327 var updater = this.bodyEl.getUpdateManager();
41328 updater.update(url, params, this._setLoaded.createDelegate(this));
41333 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41334 * Will fail silently if the setUrl method has not been called.
41335 * This does not activate the panel, just updates its content.
41337 refresh : function(){
41338 if(this.refreshDelegate){
41339 this.loaded = false;
41340 this.refreshDelegate();
41345 _setLoaded : function(){
41346 this.loaded = true;
41350 closeClick : function(e){
41353 this.fireEvent("beforeclose", this, o);
41354 if(o.cancel !== true){
41355 this.tabPanel.removeTab(this.id);
41359 * The text displayed in the tooltip for the close icon.
41362 closeText : "Close this tab"
41365 * This script refer to:
41366 * Title: International Telephone Input
41367 * Author: Jack O'Connor
41368 * Code version: v12.1.12
41369 * Availability: https://github.com/jackocnr/intl-tel-input.git
41372 Roo.bootstrap.PhoneInputData = function() {
41375 "Afghanistan (افغانستان)",
41380 "Albania (Shqipëri)",
41385 "Algeria (الجزائر)",
41410 "Antigua and Barbuda",
41420 "Armenia (Հայաստան)",
41436 "Austria (Österreich)",
41441 "Azerbaijan (Azərbaycan)",
41451 "Bahrain (البحرين)",
41456 "Bangladesh (বাংলাদেশ)",
41466 "Belarus (Беларусь)",
41471 "Belgium (België)",
41501 "Bosnia and Herzegovina (Босна и Херцеговина)",
41516 "British Indian Ocean Territory",
41521 "British Virgin Islands",
41531 "Bulgaria (България)",
41541 "Burundi (Uburundi)",
41546 "Cambodia (កម្ពុជា)",
41551 "Cameroon (Cameroun)",
41560 ["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"]
41563 "Cape Verde (Kabu Verdi)",
41568 "Caribbean Netherlands",
41579 "Central African Republic (République centrafricaine)",
41599 "Christmas Island",
41605 "Cocos (Keeling) Islands",
41616 "Comoros (جزر القمر)",
41621 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41626 "Congo (Republic) (Congo-Brazzaville)",
41646 "Croatia (Hrvatska)",
41667 "Czech Republic (Česká republika)",
41672 "Denmark (Danmark)",
41687 "Dominican Republic (República Dominicana)",
41691 ["809", "829", "849"]
41709 "Equatorial Guinea (Guinea Ecuatorial)",
41729 "Falkland Islands (Islas Malvinas)",
41734 "Faroe Islands (Føroyar)",
41755 "French Guiana (Guyane française)",
41760 "French Polynesia (Polynésie française)",
41775 "Georgia (საქართველო)",
41780 "Germany (Deutschland)",
41800 "Greenland (Kalaallit Nunaat)",
41837 "Guinea-Bissau (Guiné Bissau)",
41862 "Hungary (Magyarország)",
41867 "Iceland (Ísland)",
41887 "Iraq (العراق)",
41903 "Israel (ישראל)",
41930 "Jordan (الأردن)",
41935 "Kazakhstan (Казахстан)",
41956 "Kuwait (الكويت)",
41961 "Kyrgyzstan (Кыргызстан)",
41971 "Latvia (Latvija)",
41976 "Lebanon (لبنان)",
41991 "Libya (ليبيا)",
42001 "Lithuania (Lietuva)",
42016 "Macedonia (FYROM) (Македонија)",
42021 "Madagascar (Madagasikara)",
42051 "Marshall Islands",
42061 "Mauritania (موريتانيا)",
42066 "Mauritius (Moris)",
42087 "Moldova (Republica Moldova)",
42097 "Mongolia (Монгол)",
42102 "Montenegro (Crna Gora)",
42112 "Morocco (المغرب)",
42118 "Mozambique (Moçambique)",
42123 "Myanmar (Burma) (မြန်မာ)",
42128 "Namibia (Namibië)",
42143 "Netherlands (Nederland)",
42148 "New Caledonia (Nouvelle-Calédonie)",
42183 "North Korea (조선 민주주의 인민 공화국)",
42188 "Northern Mariana Islands",
42204 "Pakistan (پاکستان)",
42214 "Palestine (فلسطين)",
42224 "Papua New Guinea",
42266 "Réunion (La Réunion)",
42272 "Romania (România)",
42288 "Saint Barthélemy",
42299 "Saint Kitts and Nevis",
42309 "Saint Martin (Saint-Martin (partie française))",
42315 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42320 "Saint Vincent and the Grenadines",
42335 "São Tomé and Príncipe (São Tomé e Príncipe)",
42340 "Saudi Arabia (المملكة العربية السعودية)",
42345 "Senegal (Sénégal)",
42375 "Slovakia (Slovensko)",
42380 "Slovenia (Slovenija)",
42390 "Somalia (Soomaaliya)",
42400 "South Korea (대한민국)",
42405 "South Sudan (جنوب السودان)",
42415 "Sri Lanka (ශ්රී ලංකාව)",
42420 "Sudan (السودان)",
42430 "Svalbard and Jan Mayen",
42441 "Sweden (Sverige)",
42446 "Switzerland (Schweiz)",
42451 "Syria (سوريا)",
42496 "Trinidad and Tobago",
42501 "Tunisia (تونس)",
42506 "Turkey (Türkiye)",
42516 "Turks and Caicos Islands",
42526 "U.S. Virgin Islands",
42536 "Ukraine (Україна)",
42541 "United Arab Emirates (الإمارات العربية المتحدة)",
42563 "Uzbekistan (Oʻzbekiston)",
42573 "Vatican City (Città del Vaticano)",
42584 "Vietnam (Việt Nam)",
42589 "Wallis and Futuna (Wallis-et-Futuna)",
42594 "Western Sahara (الصحراء الغربية)",
42600 "Yemen (اليمن)",
42624 * This script refer to:
42625 * Title: International Telephone Input
42626 * Author: Jack O'Connor
42627 * Code version: v12.1.12
42628 * Availability: https://github.com/jackocnr/intl-tel-input.git
42632 * @class Roo.bootstrap.PhoneInput
42633 * @extends Roo.bootstrap.TriggerField
42634 * An input with International dial-code selection
42636 * @cfg {String} defaultDialCode default '+852'
42637 * @cfg {Array} preferedCountries default []
42640 * Create a new PhoneInput.
42641 * @param {Object} config Configuration options
42644 Roo.bootstrap.PhoneInput = function(config) {
42645 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42648 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42650 listWidth: undefined,
42652 selectedClass: 'active',
42654 invalidClass : "has-warning",
42656 validClass: 'has-success',
42658 allowed: '0123456789',
42663 * @cfg {String} defaultDialCode The default dial code when initializing the input
42665 defaultDialCode: '+852',
42668 * @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
42670 preferedCountries: false,
42672 getAutoCreate : function()
42674 var data = Roo.bootstrap.PhoneInputData();
42675 var align = this.labelAlign || this.parentLabelAlign();
42678 this.allCountries = [];
42679 this.dialCodeMapping = [];
42681 for (var i = 0; i < data.length; i++) {
42683 this.allCountries[i] = {
42687 priority: c[3] || 0,
42688 areaCodes: c[4] || null
42690 this.dialCodeMapping[c[2]] = {
42693 priority: c[3] || 0,
42694 areaCodes: c[4] || null
42706 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42707 maxlength: this.max_length,
42708 cls : 'form-control tel-input',
42709 autocomplete: 'new-password'
42712 var hiddenInput = {
42715 cls: 'hidden-tel-input'
42719 hiddenInput.name = this.name;
42722 if (this.disabled) {
42723 input.disabled = true;
42726 var flag_container = {
42743 cls: this.hasFeedback ? 'has-feedback' : '',
42749 cls: 'dial-code-holder',
42756 cls: 'roo-select2-container input-group',
42763 if (this.fieldLabel.length) {
42766 tooltip: 'This field is required'
42772 cls: 'control-label',
42778 html: this.fieldLabel
42781 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42787 if(this.indicatorpos == 'right') {
42788 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42795 if(align == 'left') {
42803 if(this.labelWidth > 12){
42804 label.style = "width: " + this.labelWidth + 'px';
42806 if(this.labelWidth < 13 && this.labelmd == 0){
42807 this.labelmd = this.labelWidth;
42809 if(this.labellg > 0){
42810 label.cls += ' col-lg-' + this.labellg;
42811 input.cls += ' col-lg-' + (12 - this.labellg);
42813 if(this.labelmd > 0){
42814 label.cls += ' col-md-' + this.labelmd;
42815 container.cls += ' col-md-' + (12 - this.labelmd);
42817 if(this.labelsm > 0){
42818 label.cls += ' col-sm-' + this.labelsm;
42819 container.cls += ' col-sm-' + (12 - this.labelsm);
42821 if(this.labelxs > 0){
42822 label.cls += ' col-xs-' + this.labelxs;
42823 container.cls += ' col-xs-' + (12 - this.labelxs);
42833 var settings = this;
42835 ['xs','sm','md','lg'].map(function(size){
42836 if (settings[size]) {
42837 cfg.cls += ' col-' + size + '-' + settings[size];
42841 this.store = new Roo.data.Store({
42842 proxy : new Roo.data.MemoryProxy({}),
42843 reader : new Roo.data.JsonReader({
42854 'name' : 'dialCode',
42858 'name' : 'priority',
42862 'name' : 'areaCodes',
42869 if(!this.preferedCountries) {
42870 this.preferedCountries = [
42877 var p = this.preferedCountries.reverse();
42880 for (var i = 0; i < p.length; i++) {
42881 for (var j = 0; j < this.allCountries.length; j++) {
42882 if(this.allCountries[j].iso2 == p[i]) {
42883 var t = this.allCountries[j];
42884 this.allCountries.splice(j,1);
42885 this.allCountries.unshift(t);
42891 this.store.proxy.data = {
42893 data: this.allCountries
42899 initEvents : function()
42902 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42904 this.indicator = this.indicatorEl();
42905 this.flag = this.flagEl();
42906 this.dialCodeHolder = this.dialCodeHolderEl();
42908 this.trigger = this.el.select('div.flag-box',true).first();
42909 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42914 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42915 _this.list.setWidth(lw);
42918 this.list.on('mouseover', this.onViewOver, this);
42919 this.list.on('mousemove', this.onViewMove, this);
42920 this.inputEl().on("keyup", this.onKeyUp, this);
42921 this.inputEl().on("keypress", this.onKeyPress, this);
42923 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42925 this.view = new Roo.View(this.list, this.tpl, {
42926 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42929 this.view.on('click', this.onViewClick, this);
42930 this.setValue(this.defaultDialCode);
42933 onTriggerClick : function(e)
42935 Roo.log('trigger click');
42940 if(this.isExpanded()){
42942 this.hasFocus = false;
42944 this.store.load({});
42945 this.hasFocus = true;
42950 isExpanded : function()
42952 return this.list.isVisible();
42955 collapse : function()
42957 if(!this.isExpanded()){
42961 Roo.get(document).un('mousedown', this.collapseIf, this);
42962 Roo.get(document).un('mousewheel', this.collapseIf, this);
42963 this.fireEvent('collapse', this);
42967 expand : function()
42971 if(this.isExpanded() || !this.hasFocus){
42975 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42976 this.list.setWidth(lw);
42979 this.restrictHeight();
42981 Roo.get(document).on('mousedown', this.collapseIf, this);
42982 Roo.get(document).on('mousewheel', this.collapseIf, this);
42984 this.fireEvent('expand', this);
42987 restrictHeight : function()
42989 this.list.alignTo(this.inputEl(), this.listAlign);
42990 this.list.alignTo(this.inputEl(), this.listAlign);
42993 onViewOver : function(e, t)
42995 if(this.inKeyMode){
42998 var item = this.view.findItemFromChild(t);
43001 var index = this.view.indexOf(item);
43002 this.select(index, false);
43007 onViewClick : function(view, doFocus, el, e)
43009 var index = this.view.getSelectedIndexes()[0];
43011 var r = this.store.getAt(index);
43014 this.onSelect(r, index);
43016 if(doFocus !== false && !this.blockFocus){
43017 this.inputEl().focus();
43021 onViewMove : function(e, t)
43023 this.inKeyMode = false;
43026 select : function(index, scrollIntoView)
43028 this.selectedIndex = index;
43029 this.view.select(index);
43030 if(scrollIntoView !== false){
43031 var el = this.view.getNode(index);
43033 this.list.scrollChildIntoView(el, false);
43038 createList : function()
43040 this.list = Roo.get(document.body).createChild({
43042 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43043 style: 'display:none'
43046 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43049 collapseIf : function(e)
43051 var in_combo = e.within(this.el);
43052 var in_list = e.within(this.list);
43053 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43055 if (in_combo || in_list || is_list) {
43061 onSelect : function(record, index)
43063 if(this.fireEvent('beforeselect', this, record, index) !== false){
43065 this.setFlagClass(record.data.iso2);
43066 this.setDialCode(record.data.dialCode);
43067 this.hasFocus = false;
43069 this.fireEvent('select', this, record, index);
43073 flagEl : function()
43075 var flag = this.el.select('div.flag',true).first();
43082 dialCodeHolderEl : function()
43084 var d = this.el.select('input.dial-code-holder',true).first();
43091 setDialCode : function(v)
43093 this.dialCodeHolder.dom.value = '+'+v;
43096 setFlagClass : function(n)
43098 this.flag.dom.className = 'flag '+n;
43101 getValue : function()
43103 var v = this.inputEl().getValue();
43104 if(this.dialCodeHolder) {
43105 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43110 setValue : function(v)
43112 var d = this.getDialCode(v);
43114 //invalid dial code
43115 if(v.length == 0 || !d || d.length == 0) {
43117 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43118 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43124 this.setFlagClass(this.dialCodeMapping[d].iso2);
43125 this.setDialCode(d);
43126 this.inputEl().dom.value = v.replace('+'+d,'');
43127 this.hiddenEl().dom.value = this.getValue();
43132 getDialCode : function(v)
43136 if (v.length == 0) {
43137 return this.dialCodeHolder.dom.value;
43141 if (v.charAt(0) != "+") {
43144 var numericChars = "";
43145 for (var i = 1; i < v.length; i++) {
43146 var c = v.charAt(i);
43149 if (this.dialCodeMapping[numericChars]) {
43150 dialCode = v.substr(1, i);
43152 if (numericChars.length == 4) {
43162 this.setValue(this.defaultDialCode);
43166 hiddenEl : function()
43168 return this.el.select('input.hidden-tel-input',true).first();
43171 // after setting val
43172 onKeyUp : function(e){
43173 this.setValue(this.getValue());
43176 onKeyPress : function(e){
43177 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43184 * @class Roo.bootstrap.MoneyField
43185 * @extends Roo.bootstrap.ComboBox
43186 * Bootstrap MoneyField class
43189 * Create a new MoneyField.
43190 * @param {Object} config Configuration options
43193 Roo.bootstrap.MoneyField = function(config) {
43195 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43199 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43202 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43204 allowDecimals : true,
43206 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43208 decimalSeparator : ".",
43210 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43212 decimalPrecision : 0,
43214 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43216 allowNegative : true,
43218 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43222 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43224 minValue : Number.NEGATIVE_INFINITY,
43226 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43228 maxValue : Number.MAX_VALUE,
43230 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43232 minText : "The minimum value for this field is {0}",
43234 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43236 maxText : "The maximum value for this field is {0}",
43238 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43239 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43241 nanText : "{0} is not a valid number",
43243 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43247 * @cfg {String} defaults currency of the MoneyField
43248 * value should be in lkey
43250 defaultCurrency : false,
43252 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43254 thousandsDelimiter : false,
43256 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43267 getAutoCreate : function()
43269 var align = this.labelAlign || this.parentLabelAlign();
43281 cls : 'form-control roo-money-amount-input',
43282 autocomplete: 'new-password'
43285 var hiddenInput = {
43289 cls: 'hidden-number-input'
43292 if(this.max_length) {
43293 input.maxlength = this.max_length;
43297 hiddenInput.name = this.name;
43300 if (this.disabled) {
43301 input.disabled = true;
43304 var clg = 12 - this.inputlg;
43305 var cmd = 12 - this.inputmd;
43306 var csm = 12 - this.inputsm;
43307 var cxs = 12 - this.inputxs;
43311 cls : 'row roo-money-field',
43315 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43319 cls: 'roo-select2-container input-group',
43323 cls : 'form-control roo-money-currency-input',
43324 autocomplete: 'new-password',
43326 name : this.currencyName
43330 cls : 'input-group-addon',
43344 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43348 cls: this.hasFeedback ? 'has-feedback' : '',
43359 if (this.fieldLabel.length) {
43362 tooltip: 'This field is required'
43368 cls: 'control-label',
43374 html: this.fieldLabel
43377 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43383 if(this.indicatorpos == 'right') {
43384 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43391 if(align == 'left') {
43399 if(this.labelWidth > 12){
43400 label.style = "width: " + this.labelWidth + 'px';
43402 if(this.labelWidth < 13 && this.labelmd == 0){
43403 this.labelmd = this.labelWidth;
43405 if(this.labellg > 0){
43406 label.cls += ' col-lg-' + this.labellg;
43407 input.cls += ' col-lg-' + (12 - this.labellg);
43409 if(this.labelmd > 0){
43410 label.cls += ' col-md-' + this.labelmd;
43411 container.cls += ' col-md-' + (12 - this.labelmd);
43413 if(this.labelsm > 0){
43414 label.cls += ' col-sm-' + this.labelsm;
43415 container.cls += ' col-sm-' + (12 - this.labelsm);
43417 if(this.labelxs > 0){
43418 label.cls += ' col-xs-' + this.labelxs;
43419 container.cls += ' col-xs-' + (12 - this.labelxs);
43430 var settings = this;
43432 ['xs','sm','md','lg'].map(function(size){
43433 if (settings[size]) {
43434 cfg.cls += ' col-' + size + '-' + settings[size];
43441 initEvents : function()
43443 this.indicator = this.indicatorEl();
43445 this.initCurrencyEvent();
43447 this.initNumberEvent();
43450 initCurrencyEvent : function()
43453 throw "can not find store for combo";
43456 this.store = Roo.factory(this.store, Roo.data);
43457 this.store.parent = this;
43461 this.triggerEl = this.el.select('.input-group-addon', true).first();
43463 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43468 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43469 _this.list.setWidth(lw);
43472 this.list.on('mouseover', this.onViewOver, this);
43473 this.list.on('mousemove', this.onViewMove, this);
43474 this.list.on('scroll', this.onViewScroll, this);
43477 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43480 this.view = new Roo.View(this.list, this.tpl, {
43481 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43484 this.view.on('click', this.onViewClick, this);
43486 this.store.on('beforeload', this.onBeforeLoad, this);
43487 this.store.on('load', this.onLoad, this);
43488 this.store.on('loadexception', this.onLoadException, this);
43490 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43491 "up" : function(e){
43492 this.inKeyMode = true;
43496 "down" : function(e){
43497 if(!this.isExpanded()){
43498 this.onTriggerClick();
43500 this.inKeyMode = true;
43505 "enter" : function(e){
43508 if(this.fireEvent("specialkey", this, e)){
43509 this.onViewClick(false);
43515 "esc" : function(e){
43519 "tab" : function(e){
43522 if(this.fireEvent("specialkey", this, e)){
43523 this.onViewClick(false);
43531 doRelay : function(foo, bar, hname){
43532 if(hname == 'down' || this.scope.isExpanded()){
43533 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43541 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43545 initNumberEvent : function(e)
43547 this.inputEl().on("keydown" , this.fireKey, this);
43548 this.inputEl().on("focus", this.onFocus, this);
43549 this.inputEl().on("blur", this.onBlur, this);
43551 this.inputEl().relayEvent('keyup', this);
43553 if(this.indicator){
43554 this.indicator.addClass('invisible');
43557 this.originalValue = this.getValue();
43559 if(this.validationEvent == 'keyup'){
43560 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43561 this.inputEl().on('keyup', this.filterValidation, this);
43563 else if(this.validationEvent !== false){
43564 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43567 if(this.selectOnFocus){
43568 this.on("focus", this.preFocus, this);
43571 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43572 this.inputEl().on("keypress", this.filterKeys, this);
43574 this.inputEl().relayEvent('keypress', this);
43577 var allowed = "0123456789";
43579 if(this.allowDecimals){
43580 allowed += this.decimalSeparator;
43583 if(this.allowNegative){
43587 if(this.thousandsDelimiter) {
43591 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43593 var keyPress = function(e){
43595 var k = e.getKey();
43597 var c = e.getCharCode();
43600 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43601 allowed.indexOf(String.fromCharCode(c)) === -1
43607 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43611 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43616 this.inputEl().on("keypress", keyPress, this);
43620 onTriggerClick : function(e)
43627 this.loadNext = false;
43629 if(this.isExpanded()){
43634 this.hasFocus = true;
43636 if(this.triggerAction == 'all') {
43637 this.doQuery(this.allQuery, true);
43641 this.doQuery(this.getRawValue());
43644 getCurrency : function()
43646 var v = this.currencyEl().getValue();
43651 restrictHeight : function()
43653 this.list.alignTo(this.currencyEl(), this.listAlign);
43654 this.list.alignTo(this.currencyEl(), this.listAlign);
43657 onViewClick : function(view, doFocus, el, e)
43659 var index = this.view.getSelectedIndexes()[0];
43661 var r = this.store.getAt(index);
43664 this.onSelect(r, index);
43668 onSelect : function(record, index){
43670 if(this.fireEvent('beforeselect', this, record, index) !== false){
43672 this.setFromCurrencyData(index > -1 ? record.data : false);
43676 this.fireEvent('select', this, record, index);
43680 setFromCurrencyData : function(o)
43684 this.lastCurrency = o;
43686 if (this.currencyField) {
43687 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43689 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43692 this.lastSelectionText = currency;
43694 //setting default currency
43695 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43696 this.setCurrency(this.defaultCurrency);
43700 this.setCurrency(currency);
43703 setFromData : function(o)
43707 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43709 this.setFromCurrencyData(c);
43714 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43716 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43719 this.setValue(value);
43723 setCurrency : function(v)
43725 this.currencyValue = v;
43728 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43733 setValue : function(v)
43735 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43741 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43743 this.inputEl().dom.value = (v == '') ? '' :
43744 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43746 if(!this.allowZero && v === '0') {
43747 this.hiddenEl().dom.value = '';
43748 this.inputEl().dom.value = '';
43755 getRawValue : function()
43757 var v = this.inputEl().getValue();
43762 getValue : function()
43764 return this.fixPrecision(this.parseValue(this.getRawValue()));
43767 parseValue : function(value)
43769 if(this.thousandsDelimiter) {
43771 r = new RegExp(",", "g");
43772 value = value.replace(r, "");
43775 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43776 return isNaN(value) ? '' : value;
43780 fixPrecision : function(value)
43782 if(this.thousandsDelimiter) {
43784 r = new RegExp(",", "g");
43785 value = value.replace(r, "");
43788 var nan = isNaN(value);
43790 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43791 return nan ? '' : value;
43793 return parseFloat(value).toFixed(this.decimalPrecision);
43796 decimalPrecisionFcn : function(v)
43798 return Math.floor(v);
43801 validateValue : function(value)
43803 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43807 var num = this.parseValue(value);
43810 this.markInvalid(String.format(this.nanText, value));
43814 if(num < this.minValue){
43815 this.markInvalid(String.format(this.minText, this.minValue));
43819 if(num > this.maxValue){
43820 this.markInvalid(String.format(this.maxText, this.maxValue));
43827 validate : function()
43829 if(this.disabled || this.allowBlank){
43834 var currency = this.getCurrency();
43836 if(this.validateValue(this.getRawValue()) && currency.length){
43841 this.markInvalid();
43845 getName: function()
43850 beforeBlur : function()
43856 var v = this.parseValue(this.getRawValue());
43863 onBlur : function()
43867 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43868 //this.el.removeClass(this.focusClass);
43871 this.hasFocus = false;
43873 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43877 var v = this.getValue();
43879 if(String(v) !== String(this.startValue)){
43880 this.fireEvent('change', this, v, this.startValue);
43883 this.fireEvent("blur", this);
43886 inputEl : function()
43888 return this.el.select('.roo-money-amount-input', true).first();
43891 currencyEl : function()
43893 return this.el.select('.roo-money-currency-input', true).first();
43896 hiddenEl : function()
43898 return this.el.select('input.hidden-number-input',true).first();
43902 * @class Roo.bootstrap.BezierSignature
43903 * @extends Roo.bootstrap.Component
43904 * Bootstrap BezierSignature class
43905 * This script refer to:
43906 * Title: Signature Pad
43908 * Availability: https://github.com/szimek/signature_pad
43911 * Create a new BezierSignature
43912 * @param {Object} config The config object
43915 Roo.bootstrap.BezierSignature = function(config){
43916 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43922 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43929 mouse_btn_down: true,
43932 * @cfg {int} canvas height
43934 canvas_height: '200px',
43937 * @cfg {float|function} Radius of a single dot.
43942 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43947 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43952 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43957 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43962 * @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.
43964 bg_color: 'rgba(0, 0, 0, 0)',
43967 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43969 dot_color: 'black',
43972 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43974 velocity_filter_weight: 0.7,
43977 * @cfg {function} Callback when stroke begin.
43982 * @cfg {function} Callback when stroke end.
43986 getAutoCreate : function()
43988 var cls = 'roo-signature column';
43991 cls += ' ' + this.cls;
44001 for(var i = 0; i < col_sizes.length; i++) {
44002 if(this[col_sizes[i]]) {
44003 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44013 cls: 'roo-signature-body',
44017 cls: 'roo-signature-body-canvas',
44018 height: this.canvas_height,
44019 width: this.canvas_width
44026 style: 'display: none'
44034 initEvents: function()
44036 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44038 var canvas = this.canvasEl();
44040 // mouse && touch event swapping...
44041 canvas.dom.style.touchAction = 'none';
44042 canvas.dom.style.msTouchAction = 'none';
44044 this.mouse_btn_down = false;
44045 canvas.on('mousedown', this._handleMouseDown, this);
44046 canvas.on('mousemove', this._handleMouseMove, this);
44047 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44049 if (window.PointerEvent) {
44050 canvas.on('pointerdown', this._handleMouseDown, this);
44051 canvas.on('pointermove', this._handleMouseMove, this);
44052 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44055 if ('ontouchstart' in window) {
44056 canvas.on('touchstart', this._handleTouchStart, this);
44057 canvas.on('touchmove', this._handleTouchMove, this);
44058 canvas.on('touchend', this._handleTouchEnd, this);
44061 Roo.EventManager.onWindowResize(this.resize, this, true);
44063 // file input event
44064 this.fileEl().on('change', this.uploadImage, this);
44071 resize: function(){
44073 var canvas = this.canvasEl().dom;
44074 var ctx = this.canvasElCtx();
44075 var img_data = false;
44077 if(canvas.width > 0) {
44078 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44080 // setting canvas width will clean img data
44083 var style = window.getComputedStyle ?
44084 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44086 var padding_left = parseInt(style.paddingLeft) || 0;
44087 var padding_right = parseInt(style.paddingRight) || 0;
44089 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44092 ctx.putImageData(img_data, 0, 0);
44096 _handleMouseDown: function(e)
44098 if (e.browserEvent.which === 1) {
44099 this.mouse_btn_down = true;
44100 this.strokeBegin(e);
44104 _handleMouseMove: function (e)
44106 if (this.mouse_btn_down) {
44107 this.strokeMoveUpdate(e);
44111 _handleMouseUp: function (e)
44113 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44114 this.mouse_btn_down = false;
44119 _handleTouchStart: function (e) {
44121 e.preventDefault();
44122 if (e.browserEvent.targetTouches.length === 1) {
44123 // var touch = e.browserEvent.changedTouches[0];
44124 // this.strokeBegin(touch);
44126 this.strokeBegin(e); // assume e catching the correct xy...
44130 _handleTouchMove: function (e) {
44131 e.preventDefault();
44132 // var touch = event.targetTouches[0];
44133 // _this._strokeMoveUpdate(touch);
44134 this.strokeMoveUpdate(e);
44137 _handleTouchEnd: function (e) {
44138 var wasCanvasTouched = e.target === this.canvasEl().dom;
44139 if (wasCanvasTouched) {
44140 e.preventDefault();
44141 // var touch = event.changedTouches[0];
44142 // _this._strokeEnd(touch);
44147 reset: function () {
44148 this._lastPoints = [];
44149 this._lastVelocity = 0;
44150 this._lastWidth = (this.min_width + this.max_width) / 2;
44151 this.canvasElCtx().fillStyle = this.dot_color;
44154 strokeMoveUpdate: function(e)
44156 this.strokeUpdate(e);
44158 if (this.throttle) {
44159 this.throttleStroke(this.strokeUpdate, this.throttle);
44162 this.strokeUpdate(e);
44166 strokeBegin: function(e)
44168 var newPointGroup = {
44169 color: this.dot_color,
44173 if (typeof this.onBegin === 'function') {
44177 this.curve_data.push(newPointGroup);
44179 this.strokeUpdate(e);
44182 strokeUpdate: function(e)
44184 var rect = this.canvasEl().dom.getBoundingClientRect();
44185 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44186 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44187 var lastPoints = lastPointGroup.points;
44188 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44189 var isLastPointTooClose = lastPoint
44190 ? point.distanceTo(lastPoint) <= this.min_distance
44192 var color = lastPointGroup.color;
44193 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44194 var curve = this.addPoint(point);
44196 this.drawDot({color: color, point: point});
44199 this.drawCurve({color: color, curve: curve});
44209 strokeEnd: function(e)
44211 this.strokeUpdate(e);
44212 if (typeof this.onEnd === 'function') {
44217 addPoint: function (point) {
44218 var _lastPoints = this._lastPoints;
44219 _lastPoints.push(point);
44220 if (_lastPoints.length > 2) {
44221 if (_lastPoints.length === 3) {
44222 _lastPoints.unshift(_lastPoints[0]);
44224 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44225 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44226 _lastPoints.shift();
44232 calculateCurveWidths: function (startPoint, endPoint) {
44233 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44234 (1 - this.velocity_filter_weight) * this._lastVelocity;
44236 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44239 start: this._lastWidth
44242 this._lastVelocity = velocity;
44243 this._lastWidth = newWidth;
44247 drawDot: function (_a) {
44248 var color = _a.color, point = _a.point;
44249 var ctx = this.canvasElCtx();
44250 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44252 this.drawCurveSegment(point.x, point.y, width);
44254 ctx.fillStyle = color;
44258 drawCurve: function (_a) {
44259 var color = _a.color, curve = _a.curve;
44260 var ctx = this.canvasElCtx();
44261 var widthDelta = curve.endWidth - curve.startWidth;
44262 var drawSteps = Math.floor(curve.length()) * 2;
44264 ctx.fillStyle = color;
44265 for (var i = 0; i < drawSteps; i += 1) {
44266 var t = i / drawSteps;
44272 var x = uuu * curve.startPoint.x;
44273 x += 3 * uu * t * curve.control1.x;
44274 x += 3 * u * tt * curve.control2.x;
44275 x += ttt * curve.endPoint.x;
44276 var y = uuu * curve.startPoint.y;
44277 y += 3 * uu * t * curve.control1.y;
44278 y += 3 * u * tt * curve.control2.y;
44279 y += ttt * curve.endPoint.y;
44280 var width = curve.startWidth + ttt * widthDelta;
44281 this.drawCurveSegment(x, y, width);
44287 drawCurveSegment: function (x, y, width) {
44288 var ctx = this.canvasElCtx();
44290 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44291 this.is_empty = false;
44296 var ctx = this.canvasElCtx();
44297 var canvas = this.canvasEl().dom;
44298 ctx.fillStyle = this.bg_color;
44299 ctx.clearRect(0, 0, canvas.width, canvas.height);
44300 ctx.fillRect(0, 0, canvas.width, canvas.height);
44301 this.curve_data = [];
44303 this.is_empty = true;
44308 return this.el.select('input',true).first();
44311 canvasEl: function()
44313 return this.el.select('canvas',true).first();
44316 canvasElCtx: function()
44318 return this.el.select('canvas',true).first().dom.getContext('2d');
44321 getImage: function(type)
44323 if(this.is_empty) {
44328 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44331 drawFromImage: function(img_src)
44333 var img = new Image();
44335 img.onload = function(){
44336 this.canvasElCtx().drawImage(img, 0, 0);
44341 this.is_empty = false;
44344 selectImage: function()
44346 this.fileEl().dom.click();
44349 uploadImage: function(e)
44351 var reader = new FileReader();
44353 reader.onload = function(e){
44354 var img = new Image();
44355 img.onload = function(){
44357 this.canvasElCtx().drawImage(img, 0, 0);
44359 img.src = e.target.result;
44362 reader.readAsDataURL(e.target.files[0]);
44365 // Bezier Point Constructor
44366 Point: (function () {
44367 function Point(x, y, time) {
44370 this.time = time || Date.now();
44372 Point.prototype.distanceTo = function (start) {
44373 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44375 Point.prototype.equals = function (other) {
44376 return this.x === other.x && this.y === other.y && this.time === other.time;
44378 Point.prototype.velocityFrom = function (start) {
44379 return this.time !== start.time
44380 ? this.distanceTo(start) / (this.time - start.time)
44387 // Bezier Constructor
44388 Bezier: (function () {
44389 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44390 this.startPoint = startPoint;
44391 this.control2 = control2;
44392 this.control1 = control1;
44393 this.endPoint = endPoint;
44394 this.startWidth = startWidth;
44395 this.endWidth = endWidth;
44397 Bezier.fromPoints = function (points, widths, scope) {
44398 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44399 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44400 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44402 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44403 var dx1 = s1.x - s2.x;
44404 var dy1 = s1.y - s2.y;
44405 var dx2 = s2.x - s3.x;
44406 var dy2 = s2.y - s3.y;
44407 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44408 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44409 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44410 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44411 var dxm = m1.x - m2.x;
44412 var dym = m1.y - m2.y;
44413 var k = l2 / (l1 + l2);
44414 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44415 var tx = s2.x - cm.x;
44416 var ty = s2.y - cm.y;
44418 c1: new scope.Point(m1.x + tx, m1.y + ty),
44419 c2: new scope.Point(m2.x + tx, m2.y + ty)
44422 Bezier.prototype.length = function () {
44427 for (var i = 0; i <= steps; i += 1) {
44429 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44430 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44432 var xdiff = cx - px;
44433 var ydiff = cy - py;
44434 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44441 Bezier.prototype.point = function (t, start, c1, c2, end) {
44442 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44443 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44444 + (3.0 * c2 * (1.0 - t) * t * t)
44445 + (end * t * t * t);
44450 throttleStroke: function(fn, wait) {
44451 if (wait === void 0) { wait = 250; }
44453 var timeout = null;
44457 var later = function () {
44458 previous = Date.now();
44460 result = fn.apply(storedContext, storedArgs);
44462 storedContext = null;
44466 return function wrapper() {
44468 for (var _i = 0; _i < arguments.length; _i++) {
44469 args[_i] = arguments[_i];
44471 var now = Date.now();
44472 var remaining = wait - (now - previous);
44473 storedContext = this;
44475 if (remaining <= 0 || remaining > wait) {
44477 clearTimeout(timeout);
44481 result = fn.apply(storedContext, storedArgs);
44483 storedContext = null;
44487 else if (!timeout) {
44488 timeout = window.setTimeout(later, remaining);