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)
100 * @cfg {String} offset
101 * The number of pixels to offset the shadow from the element (defaults to 4)
109 * Displays the shadow under the target element
110 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
112 show : function(target){
113 target = Roo.get(target);
115 this.el = Roo.Shadow.Pool.pull();
116 if(this.el.dom.nextSibling != target.dom){
117 this.el.insertBefore(target);
120 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
122 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
125 target.getLeft(true),
130 this.el.dom.style.display = "block";
134 * Returns true if the shadow is visible, else false
136 isVisible : function(){
137 return this.el ? true : false;
141 * Direct alignment when values are already available. Show must be called at least once before
142 * calling this method to ensure it is initialized.
143 * @param {Number} left The target element left position
144 * @param {Number} top The target element top position
145 * @param {Number} width The target element width
146 * @param {Number} height The target element height
148 realign : function(l, t, w, h){
152 var a = this.adjusts, d = this.el.dom, s = d.style;
154 s.left = (l+a.l)+"px";
155 s.top = (t+a.t)+"px";
156 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
158 if(s.width != sws || s.height != shs){
162 var cn = d.childNodes;
163 var sww = Math.max(0, (sw-12))+"px";
164 cn[0].childNodes[1].style.width = sww;
165 cn[1].childNodes[1].style.width = sww;
166 cn[2].childNodes[1].style.width = sww;
167 cn[1].style.height = Math.max(0, (sh-12))+"px";
177 this.el.dom.style.display = "none";
178 Roo.Shadow.Pool.push(this.el);
184 * Adjust the z-index of this shadow
185 * @param {Number} zindex The new z-index
187 setZIndex : function(z){
190 this.el.setStyle("z-index", z);
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
198 var markup = Roo.isIE ?
199 '<div class="x-ie-shadow"></div>' :
200 '<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>';
205 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206 sh.autoBoxAdjust = false;
218 * base class for bootstrap elements.
222 Roo.bootstrap = Roo.bootstrap || {};
224 * @class Roo.bootstrap.Component
225 * @extends Roo.Component
227 * @children Roo.bootstrap.Component
228 * Bootstrap Component base class
229 * @cfg {String} cls css class
230 * @cfg {String} style any extra css
231 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
232 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
233 * @cfg {string} dataId cutomer id
234 * @cfg {string} name Specifies name attribute
235 * @cfg {string} tooltip Text for the tooltip
236 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
237 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
240 * Do not use directly - it does not do anything..
241 * @param {Object} config The config object
246 Roo.bootstrap.Component = function(config){
247 Roo.bootstrap.Component.superclass.constructor.call(this, config);
251 * @event childrenrendered
252 * Fires when the children have been rendered..
253 * @param {Roo.bootstrap.Component} this
255 "childrenrendered" : true
264 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
267 allowDomMove : false, // to stop relocations in parent onRender...
277 * Initialize Events for the element
279 initEvents : function() { },
285 can_build_overlaid : true,
287 container_method : false,
294 // returns the parent component..
295 return Roo.ComponentMgr.get(this.parentId)
301 onRender : function(ct, position)
303 // Roo.log("Call onRender: " + this.xtype);
305 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
308 if (this.el.attr('xtype')) {
309 this.el.attr('xtypex', this.el.attr('xtype'));
310 this.el.dom.removeAttribute('xtype');
320 var cfg = Roo.apply({}, this.getAutoCreate());
322 cfg.id = this.id || Roo.id();
324 // fill in the extra attributes
325 if (this.xattr && typeof(this.xattr) =='object') {
326 for (var i in this.xattr) {
327 cfg[i] = this.xattr[i];
332 cfg.dataId = this.dataId;
336 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
339 if (this.style) { // fixme needs to support more complex style data.
340 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344 cfg.name = this.name;
347 this.el = ct.createChild(cfg, position);
350 this.tooltipEl().attr('tooltip', this.tooltip);
353 if(this.tabIndex !== undefined){
354 this.el.dom.setAttribute('tabIndex', this.tabIndex);
361 * Fetch the element to add children to
362 * @return {Roo.Element} defaults to this.el
364 getChildContainer : function()
368 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
370 return Roo.get(document.body);
374 * Fetch the element to display the tooltip on.
375 * @return {Roo.Element} defaults to this.el
377 tooltipEl : function()
382 addxtype : function(tree,cntr)
386 cn = Roo.factory(tree);
387 //Roo.log(['addxtype', cn]);
389 cn.parentType = this.xtype; //??
390 cn.parentId = this.id;
392 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
393 if (typeof(cn.container_method) == 'string') {
394 cntr = cn.container_method;
398 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
400 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
402 var build_from_html = Roo.XComponent.build_from_html;
404 var is_body = (tree.xtype == 'Body') ;
406 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
408 var self_cntr_el = Roo.get(this[cntr](false));
410 // do not try and build conditional elements
411 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
416 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
417 return this.addxtypeChild(tree,cntr, is_body);
420 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
423 return this.addxtypeChild(Roo.apply({}, tree),cntr);
426 Roo.log('skipping render');
432 if (!build_from_html) {
436 // this i think handles overlaying multiple children of the same type
437 // with the sam eelement.. - which might be buggy..
439 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
445 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
456 addxtypeChild : function (tree, cntr, is_body)
458 Roo.debug && Roo.log('addxtypeChild:' + cntr);
460 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
463 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
464 (typeof(tree['flexy:foreach']) != 'undefined');
468 skip_children = false;
469 // render the element if it's not BODY.
472 // if parent was disabled, then do not try and create the children..
473 if(!this[cntr](true)){
478 cn = Roo.factory(tree);
480 cn.parentType = this.xtype; //??
481 cn.parentId = this.id;
483 var build_from_html = Roo.XComponent.build_from_html;
486 // does the container contain child eleemnts with 'xtype' attributes.
487 // that match this xtype..
488 // note - when we render we create these as well..
489 // so we should check to see if body has xtype set.
490 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
492 var self_cntr_el = Roo.get(this[cntr](false));
493 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
495 //Roo.log(Roo.XComponent.build_from_html);
496 //Roo.log("got echild:");
499 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
500 // and are not displayed -this causes this to use up the wrong element when matching.
501 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
504 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
505 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
511 //echild.dom.removeAttribute('xtype');
513 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
514 Roo.debug && Roo.log(self_cntr_el);
515 Roo.debug && Roo.log(echild);
516 Roo.debug && Roo.log(cn);
522 // if object has flexy:if - then it may or may not be rendered.
523 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
524 // skip a flexy if element.
525 Roo.debug && Roo.log('skipping render');
526 Roo.debug && Roo.log(tree);
528 Roo.debug && Roo.log('skipping all children');
529 skip_children = true;
534 // actually if flexy:foreach is found, we really want to create
535 // multiple copies here...
537 //Roo.log(this[cntr]());
538 // some elements do not have render methods.. like the layouts...
540 if(this[cntr](true) === false){
545 cn.render && cn.render(this[cntr](true));
548 // then add the element..
555 if (typeof (tree.menu) != 'undefined') {
556 tree.menu.parentType = cn.xtype;
557 tree.menu.triggerEl = cn.el;
558 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562 if (!tree.items || !tree.items.length) {
564 //Roo.log(["no children", this]);
569 var items = tree.items;
572 //Roo.log(items.length);
574 if (!skip_children) {
575 for(var i =0;i < items.length;i++) {
576 // Roo.log(['add child', items[i]]);
577 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
583 //Roo.log("fire childrenrendered");
585 cn.fireEvent('childrenrendered', this);
591 * Set the element that will be used to show or hide
593 setVisibilityEl : function(el)
595 this.visibilityEl = el;
599 * Get the element that will be used to show or hide
601 getVisibilityEl : function()
603 if (typeof(this.visibilityEl) == 'object') {
604 return this.visibilityEl;
607 if (typeof(this.visibilityEl) == 'string') {
608 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
615 * Show a component - removes 'hidden' class
619 if(!this.getVisibilityEl()){
623 this.getVisibilityEl().removeClass(['hidden','d-none']);
625 this.fireEvent('show', this);
630 * Hide a component - adds 'hidden' class
634 if(!this.getVisibilityEl()){
638 this.getVisibilityEl().addClass(['hidden','d-none']);
640 this.fireEvent('hide', this);
653 * @class Roo.bootstrap.Element
654 * @extends Roo.bootstrap.Component
655 * @children Roo.bootstrap.Component
656 * Bootstrap Element class (basically a DIV used to make random stuff )
658 * @cfg {String} html contents of the element
659 * @cfg {String} tag tag of the element
660 * @cfg {String} cls class of the element
661 * @cfg {Boolean} preventDefault (true|false) default false
662 * @cfg {Boolean} clickable (true|false) default false
663 * @cfg {String} role default blank - set to button to force cursor pointer
667 * Create a new Element
668 * @param {Object} config The config object
671 Roo.bootstrap.Element = function(config){
672 Roo.bootstrap.Element.superclass.constructor.call(this, config);
678 * When a element is chick
679 * @param {Roo.bootstrap.Element} this
680 * @param {Roo.EventObject} e
688 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
693 preventDefault: false,
698 getAutoCreate : function(){
702 // cls: this.cls, double assign in parent class Component.js :: onRender
705 if (this.role !== false) {
706 cfg.role = this.role;
712 initEvents: function()
714 Roo.bootstrap.Element.superclass.initEvents.call(this);
717 this.el.on('click', this.onClick, this);
723 onClick : function(e)
725 if(this.preventDefault){
729 this.fireEvent('click', this, e); // why was this double click before?
737 getValue : function()
739 return this.el.dom.innerHTML;
742 setValue : function(value)
744 this.el.dom.innerHTML = value;
759 * @class Roo.bootstrap.DropTarget
760 * @extends Roo.bootstrap.Element
761 * Bootstrap DropTarget class
763 * @cfg {string} name dropable name
766 * Create a new Dropable Area
767 * @param {Object} config The config object
770 Roo.bootstrap.DropTarget = function(config){
771 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
777 * When a element is chick
778 * @param {Roo.bootstrap.Element} this
779 * @param {Roo.EventObject} e
785 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
788 getAutoCreate : function(){
793 initEvents: function()
795 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
796 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
799 drop : this.dragDrop.createDelegate(this),
800 enter : this.dragEnter.createDelegate(this),
801 out : this.dragOut.createDelegate(this),
802 over : this.dragOver.createDelegate(this)
806 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
809 dragDrop : function(source,e,data)
811 // user has to decide how to impliment this.
814 //this.fireEvent('drop', this, source, e ,data);
818 dragEnter : function(n, dd, e, data)
820 // probably want to resize the element to match the dropped element..
822 this.originalSize = this.el.getSize();
823 this.el.setSize( n.el.getSize());
824 this.dropZone.DDM.refreshCache(this.name);
825 Roo.log([n, dd, e, data]);
828 dragOut : function(value)
830 // resize back to normal
832 this.el.setSize(this.originalSize);
833 this.dropZone.resetConstraints();
836 dragOver : function()
853 * @class Roo.bootstrap.Body
854 * @extends Roo.bootstrap.Component
856 * @children Roo.bootstrap.Component
858 * Bootstrap Body class
862 * @param {Object} config The config object
865 Roo.bootstrap.Body = function(config){
867 config = config || {};
869 Roo.bootstrap.Body.superclass.constructor.call(this, config);
870 this.el = Roo.get(config.el ? config.el : document.body );
871 if (this.cls && this.cls.length) {
872 Roo.get(document.body).addClass(this.cls);
876 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
878 is_body : true,// just to make sure it's constructed?
883 onRender : function(ct, position)
885 /* Roo.log("Roo.bootstrap.Body - onRender");
886 if (this.cls && this.cls.length) {
887 Roo.get(document.body).addClass(this.cls);
906 * @class Roo.bootstrap.ButtonGroup
907 * @extends Roo.bootstrap.Component
908 * Bootstrap ButtonGroup class
909 * @children Roo.bootstrap.Button Roo.bootstrap.Form
911 * @cfg {String} size lg | sm | xs (default empty normal)
912 * @cfg {String} align vertical | justified (default none)
913 * @cfg {String} direction up | down (default down)
914 * @cfg {Boolean} toolbar false | true
915 * @cfg {Boolean} btn true | false
920 * @param {Object} config The config object
923 Roo.bootstrap.ButtonGroup = function(config){
924 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
935 getAutoCreate : function(){
941 cfg.html = this.html || cfg.html;
952 if (['vertical','justified'].indexOf(this.align)!==-1) {
953 cfg.cls = 'btn-group-' + this.align;
955 if (this.align == 'justified') {
956 console.log(this.items);
960 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
961 cfg.cls += ' btn-group-' + this.size;
964 if (this.direction == 'up') {
965 cfg.cls += ' dropup' ;
971 * Add a button to the group (similar to NavItem API.)
973 addItem : function(cfg)
975 var cn = new Roo.bootstrap.Button(cfg);
977 cn.parentId = this.id;
978 cn.onRender(this.el, null);
992 * @class Roo.bootstrap.Button
993 * @extends Roo.bootstrap.Component
994 * Bootstrap Button class
995 * @cfg {String} html The button content
996 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
997 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
998 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
999 * @cfg {String} size (lg|sm|xs)
1000 * @cfg {String} tag (a|input|submit)
1001 * @cfg {String} href empty or href
1002 * @cfg {Boolean} disabled default false;
1003 * @cfg {Boolean} isClose default false;
1004 * @cfg {String} glyphicon depricated - use fa
1005 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1006 * @cfg {String} badge text for badge
1007 * @cfg {String} theme (default|glow)
1008 * @cfg {Boolean} inverse dark themed version
1009 * @cfg {Boolean} toggle is it a slidy toggle button
1010 * @cfg {Boolean} pressed default null - if the button ahs active state
1011 * @cfg {String} ontext text for on slidy toggle state
1012 * @cfg {String} offtext text for off slidy toggle state
1013 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1014 * @cfg {Boolean} removeClass remove the standard class..
1015 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1016 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1017 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1020 * Create a new button
1021 * @param {Object} config The config object
1025 Roo.bootstrap.Button = function(config){
1026 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1032 * When a button is pressed
1033 * @param {Roo.bootstrap.Button} btn
1034 * @param {Roo.EventObject} e
1039 * When a button is double clicked
1040 * @param {Roo.bootstrap.Button} btn
1041 * @param {Roo.EventObject} e
1046 * After the button has been toggles
1047 * @param {Roo.bootstrap.Button} btn
1048 * @param {Roo.EventObject} e
1049 * @param {boolean} pressed (also available as button.pressed)
1055 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1076 preventDefault: true,
1085 getAutoCreate : function(){
1093 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1094 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1095 this.tag = 'button';
1099 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1101 if (this.toggle == true) {
1104 cls: 'slider-frame roo-button',
1108 'data-on-text':'ON',
1109 'data-off-text':'OFF',
1110 cls: 'slider-button',
1115 // why are we validating the weights?
1116 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1117 cfg.cls += ' ' + this.weight;
1124 cfg.cls += ' close';
1126 cfg["aria-hidden"] = true;
1128 cfg.html = "×";
1134 if (this.theme==='default') {
1135 cfg.cls = 'btn roo-button';
1137 //if (this.parentType != 'Navbar') {
1138 this.weight = this.weight.length ? this.weight : 'default';
1140 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1142 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1143 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1144 cfg.cls += ' btn-' + outline + weight;
1145 if (this.weight == 'default') {
1147 cfg.cls += ' btn-' + this.weight;
1150 } else if (this.theme==='glow') {
1153 cfg.cls = 'btn-glow roo-button';
1155 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1157 cfg.cls += ' ' + this.weight;
1163 this.cls += ' inverse';
1167 if (this.active || this.pressed === true) {
1168 cfg.cls += ' active';
1171 if (this.disabled) {
1172 cfg.disabled = 'disabled';
1176 Roo.log('changing to ul' );
1178 this.glyphicon = 'caret';
1179 if (Roo.bootstrap.version == 4) {
1180 this.fa = 'caret-down';
1185 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1187 //gsRoo.log(this.parentType);
1188 if (this.parentType === 'Navbar' && !this.parent().bar) {
1189 Roo.log('changing to li?');
1198 href : this.href || '#'
1201 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1202 cfg.cls += ' dropdown';
1209 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1211 if (this.glyphicon) {
1212 cfg.html = ' ' + cfg.html;
1217 cls: 'glyphicon glyphicon-' + this.glyphicon
1222 cfg.html = ' ' + cfg.html;
1227 cls: 'fa fas fa-' + this.fa
1237 // cfg.cls='btn roo-button';
1241 var value = cfg.html;
1246 cls: 'glyphicon glyphicon-' + this.glyphicon,
1253 cls: 'fa fas fa-' + this.fa,
1258 var bw = this.badge_weight.length ? this.badge_weight :
1259 (this.weight.length ? this.weight : 'secondary');
1260 bw = bw == 'default' ? 'secondary' : bw;
1266 cls: 'badge badge-' + bw,
1275 cfg.cls += ' dropdown';
1276 cfg.html = typeof(cfg.html) != 'undefined' ?
1277 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280 if (cfg.tag !== 'a' && this.href !== '') {
1281 throw "Tag must be a to set href.";
1282 } else if (this.href.length > 0) {
1283 cfg.href = this.href;
1286 if(this.removeClass){
1291 cfg.target = this.target;
1296 initEvents: function() {
1297 // Roo.log('init events?');
1298 // Roo.log(this.el.dom);
1301 if (typeof (this.menu) != 'undefined') {
1302 this.menu.parentType = this.xtype;
1303 this.menu.triggerEl = this.el;
1304 this.addxtype(Roo.apply({}, this.menu));
1308 if (this.el.hasClass('roo-button')) {
1309 this.el.on('click', this.onClick, this);
1310 this.el.on('dblclick', this.onDblClick, this);
1312 this.el.select('.roo-button').on('click', this.onClick, this);
1313 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1317 if(this.removeClass){
1318 this.el.on('click', this.onClick, this);
1321 if (this.group === true) {
1322 if (this.pressed === false || this.pressed === true) {
1325 this.pressed = false;
1326 this.setActive(this.pressed);
1331 this.el.enableDisplayMode();
1334 onClick : function(e)
1336 if (this.disabled) {
1340 Roo.log('button on click ');
1341 if(this.preventDefault){
1350 this.setActive(true);
1351 var pi = this.parent().items;
1352 for (var i = 0;i < pi.length;i++) {
1353 if (this == pi[i]) {
1356 if (pi[i].el.hasClass('roo-button')) {
1357 pi[i].setActive(false);
1360 this.fireEvent('click', this, e);
1364 if (this.pressed === true || this.pressed === false) {
1365 this.toggleActive(e);
1369 this.fireEvent('click', this, e);
1371 onDblClick: function(e)
1373 if (this.disabled) {
1376 if(this.preventDefault){
1379 this.fireEvent('dblclick', this, e);
1382 * Enables this button
1386 this.disabled = false;
1387 this.el.removeClass('disabled');
1388 this.el.dom.removeAttribute("disabled");
1392 * Disable this button
1394 disable : function()
1396 this.disabled = true;
1397 this.el.addClass('disabled');
1398 this.el.attr("disabled", "disabled")
1401 * sets the active state on/off,
1402 * @param {Boolean} state (optional) Force a particular state
1404 setActive : function(v) {
1406 this.el[v ? 'addClass' : 'removeClass']('active');
1410 * toggles the current active state
1412 toggleActive : function(e)
1414 this.setActive(!this.pressed); // this modifies pressed...
1415 this.fireEvent('toggle', this, e, this.pressed);
1418 * get the current active state
1419 * @return {boolean} true if it's active
1421 isActive : function()
1423 return this.el.hasClass('active');
1426 * set the text of the first selected button
1428 setText : function(str)
1430 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433 * get the text of the first selected button
1435 getText : function()
1437 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440 setWeight : function(str)
1442 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1443 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1445 var outline = this.outline ? 'outline-' : '';
1446 if (str == 'default') {
1447 this.el.addClass('btn-default btn-outline-secondary');
1450 this.el.addClass('btn-' + outline + str);
1455 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1457 Roo.bootstrap.Button.weights = [
1477 * @class Roo.bootstrap.Column
1478 * @extends Roo.bootstrap.Component
1479 * @children Roo.bootstrap.Component
1480 * Bootstrap Column class
1481 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1482 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1483 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1484 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1485 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1486 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1487 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1488 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491 * @cfg {Boolean} hidden (true|false) hide the element
1492 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1493 * @cfg {String} fa (ban|check|...) font awesome icon
1494 * @cfg {Number} fasize (1|2|....) font awsome size
1496 * @cfg {String} icon (info-sign|check|...) glyphicon name
1498 * @cfg {String} html content of column.
1501 * Create a new Column
1502 * @param {Object} config The config object
1505 Roo.bootstrap.Column = function(config){
1506 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1527 getAutoCreate : function(){
1528 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1536 var sizes = ['xs','sm','md','lg'];
1537 sizes.map(function(size ,ix){
1538 //Roo.log( size + ':' + settings[size]);
1540 if (settings[size+'off'] !== false) {
1541 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544 if (settings[size] === false) {
1548 if (!settings[size]) { // 0 = hidden
1549 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1551 for (var i = ix; i > -1; i--) {
1552 cfg.cls += ' d-' + sizes[i] + '-none';
1558 cfg.cls += ' col-' + size + '-' + settings[size] + (
1559 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1565 cfg.cls += ' hidden';
1568 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1569 cfg.cls +=' alert alert-' + this.alert;
1573 if (this.html.length) {
1574 cfg.html = this.html;
1578 if (this.fasize > 1) {
1579 fasize = ' fa-' + this.fasize + 'x';
1581 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1586 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1605 * @class Roo.bootstrap.Container
1606 * @extends Roo.bootstrap.Component
1608 * @children Roo.bootstrap.Component
1609 * Bootstrap Container class
1610 * @cfg {Boolean} jumbotron is it a jumbotron element
1611 * @cfg {String} html content of element
1612 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1613 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1614 * @cfg {String} header content of header (for panel)
1615 * @cfg {String} footer content of footer (for panel)
1616 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1617 * @cfg {String} tag (header|aside|section) type of HTML tag.
1618 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1619 * @cfg {String} fa font awesome icon
1620 * @cfg {String} icon (info-sign|check|...) glyphicon name
1621 * @cfg {Boolean} hidden (true|false) hide the element
1622 * @cfg {Boolean} expandable (true|false) default false
1623 * @cfg {Boolean} expanded (true|false) default true
1624 * @cfg {String} rheader contet on the right of header
1625 * @cfg {Boolean} clickable (true|false) default false
1629 * Create a new Container
1630 * @param {Object} config The config object
1633 Roo.bootstrap.Container = function(config){
1634 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1640 * After the panel has been expand
1642 * @param {Roo.bootstrap.Container} this
1647 * After the panel has been collapsed
1649 * @param {Roo.bootstrap.Container} this
1654 * When a element is chick
1655 * @param {Roo.bootstrap.Container} this
1656 * @param {Roo.EventObject} e
1662 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1680 getChildContainer : function() {
1686 if (this.panel.length) {
1687 return this.el.select('.panel-body',true).first();
1694 getAutoCreate : function(){
1697 tag : this.tag || 'div',
1701 if (this.jumbotron) {
1702 cfg.cls = 'jumbotron';
1707 // - this is applied by the parent..
1709 // cfg.cls = this.cls + '';
1712 if (this.sticky.length) {
1714 var bd = Roo.get(document.body);
1715 if (!bd.hasClass('bootstrap-sticky')) {
1716 bd.addClass('bootstrap-sticky');
1717 Roo.select('html',true).setStyle('height', '100%');
1720 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1724 if (this.well.length) {
1725 switch (this.well) {
1728 cfg.cls +=' well well-' +this.well;
1737 cfg.cls += ' hidden';
1741 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1742 cfg.cls +=' alert alert-' + this.alert;
1747 if (this.panel.length) {
1748 cfg.cls += ' panel panel-' + this.panel;
1750 if (this.header.length) {
1754 if(this.expandable){
1756 cfg.cls = cfg.cls + ' expandable';
1760 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1768 cls : 'panel-title',
1769 html : (this.expandable ? ' ' : '') + this.header
1773 cls: 'panel-header-right',
1779 cls : 'panel-heading',
1780 style : this.expandable ? 'cursor: pointer' : '',
1788 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1793 if (this.footer.length) {
1795 cls : 'panel-footer',
1804 body.html = this.html || cfg.html;
1805 // prefix with the icons..
1807 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1815 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1816 cfg.cls = 'container';
1822 initEvents: function()
1824 if(this.expandable){
1825 var headerEl = this.headerEl();
1828 headerEl.on('click', this.onToggleClick, this);
1833 this.el.on('click', this.onClick, this);
1838 onToggleClick : function()
1840 var headerEl = this.headerEl();
1856 if(this.fireEvent('expand', this)) {
1858 this.expanded = true;
1860 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1862 this.el.select('.panel-body',true).first().removeClass('hide');
1864 var toggleEl = this.toggleEl();
1870 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1875 collapse : function()
1877 if(this.fireEvent('collapse', this)) {
1879 this.expanded = false;
1881 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1882 this.el.select('.panel-body',true).first().addClass('hide');
1884 var toggleEl = this.toggleEl();
1890 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1894 toggleEl : function()
1896 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1900 return this.el.select('.panel-heading .fa',true).first();
1903 headerEl : function()
1905 if(!this.el || !this.panel.length || !this.header.length){
1909 return this.el.select('.panel-heading',true).first()
1914 if(!this.el || !this.panel.length){
1918 return this.el.select('.panel-body',true).first()
1921 titleEl : function()
1923 if(!this.el || !this.panel.length || !this.header.length){
1927 return this.el.select('.panel-title',true).first();
1930 setTitle : function(v)
1932 var titleEl = this.titleEl();
1938 titleEl.dom.innerHTML = v;
1941 getTitle : function()
1944 var titleEl = this.titleEl();
1950 return titleEl.dom.innerHTML;
1953 setRightTitle : function(v)
1955 var t = this.el.select('.panel-header-right',true).first();
1961 t.dom.innerHTML = v;
1964 onClick : function(e)
1968 this.fireEvent('click', this, e);
1973 * @class Roo.bootstrap.Card
1974 * @extends Roo.bootstrap.Component
1975 * @children Roo.bootstrap.Component
1977 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980 * possible... may not be implemented..
1981 * @cfg {String} header_image src url of image.
1982 * @cfg {String|Object} header
1983 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1984 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1986 * @cfg {String} title
1987 * @cfg {String} subtitle
1988 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1989 * @cfg {String} footer
1991 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1993 * @cfg {String} margin (0|1|2|3|4|5|auto)
1994 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1995 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1996 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2001 * @cfg {String} padding (0|1|2|3|4|5)
2002 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2003 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2004 * @cfg {String} padding_left (0|1|2|3|4|5)
2005 * @cfg {String} padding_right (0|1|2|3|4|5)
2006 * @cfg {String} padding_x (0|1|2|3|4|5)
2007 * @cfg {String} padding_y (0|1|2|3|4|5)
2009 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2010 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2011 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @config {Boolean} dragable if this card can be dragged.
2016 * @config {String} drag_group group for drag
2017 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2018 * @config {String} drop_group group for drag
2020 * @config {Boolean} collapsable can the body be collapsed.
2021 * @config {Boolean} collapsed is the body collapsed when rendered...
2022 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2023 * @config {Boolean} rotated is the body rotated when rendered...
2026 * Create a new Container
2027 * @param {Object} config The config object
2030 Roo.bootstrap.Card = function(config){
2031 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2037 * When a element a card is dropped
2038 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2042 * @param {String} position 'above' or 'below'
2043 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2049 * When a element a card is rotate
2050 * @param {Roo.bootstrap.Card} this
2051 * @param {Roo.Element} n the node being dropped?
2052 * @param {Boolean} rotate status
2057 * When a card element is dragged over ready to drop (return false to block dropable)
2058 * @param {Roo.bootstrap.Card} this
2059 * @param {Object} data from dragdrop
2067 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2072 margin: '', /// may be better in component?
2102 collapsable : false,
2111 childContainer : false,
2112 dropEl : false, /// the dom placeholde element that indicates drop location.
2113 containerEl: false, // body container
2114 bodyEl: false, // card-body
2115 headerContainerEl : false, //
2117 header_imageEl : false,
2120 layoutCls : function()
2124 Roo.log(this.margin_bottom.length);
2125 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2126 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2128 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2129 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2131 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2132 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2136 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2137 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2138 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2142 // more generic support?
2150 // Roo.log("Call onRender: " + this.xtype);
2151 /* We are looking at something like this.
2153 <img src="..." class="card-img-top" alt="...">
2154 <div class="card-body">
2155 <h5 class="card-title">Card title</h5>
2156 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2158 >> this bit is really the body...
2159 <div> << we will ad dthis in hopefully it will not break shit.
2161 ** card text does not actually have any styling...
2163 <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>
2166 <a href="#" class="card-link">Card link</a>
2169 <div class="card-footer">
2170 <small class="text-muted">Last updated 3 mins ago</small>
2174 getAutoCreate : function(){
2182 if (this.weight.length && this.weight != 'light') {
2183 cfg.cls += ' text-white';
2185 cfg.cls += ' text-dark'; // need as it's nested..
2187 if (this.weight.length) {
2188 cfg.cls += ' bg-' + this.weight;
2191 cfg.cls += ' ' + this.layoutCls();
2194 var hdr_ctr = false;
2195 if (this.header.length) {
2197 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2198 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2206 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2212 if (this.collapsable) {
2215 cls : 'd-block user-select-none',
2219 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2224 hdr.cn.push(hdr_ctr);
2229 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2234 if (this.header_image.length) {
2237 cls : 'card-img-top',
2238 src: this.header_image // escape?
2243 cls : 'card-img-top d-none'
2249 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2253 if (this.collapsable || this.rotateable) {
2256 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2263 if (this.title.length) {
2267 src: this.title // escape?
2271 if (this.subtitle.length) {
2275 src: this.subtitle // escape?
2281 cls : 'roo-card-body-ctr'
2284 if (this.html.length) {
2290 // fixme ? handle objects?
2292 if (this.footer.length) {
2295 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2300 cfg.cn.push({cls : 'card-footer d-none'});
2309 getCardHeader : function()
2311 var ret = this.el.select('.card-header',true).first();
2312 if (ret.hasClass('d-none')) {
2313 ret.removeClass('d-none');
2318 getCardFooter : function()
2320 var ret = this.el.select('.card-footer',true).first();
2321 if (ret.hasClass('d-none')) {
2322 ret.removeClass('d-none');
2327 getCardImageTop : function()
2329 var ret = this.header_imageEl;
2330 if (ret.hasClass('d-none')) {
2331 ret.removeClass('d-none');
2337 getChildContainer : function()
2343 return this.el.select('.roo-card-body-ctr',true).first();
2346 initEvents: function()
2348 this.bodyEl = this.el.select('.card-body',true).first();
2349 this.containerEl = this.getChildContainer();
2351 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2352 containerScroll: true,
2353 ddGroup: this.drag_group || 'default_card_drag_group'
2355 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2357 if (this.dropable) {
2358 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2359 containerScroll: true,
2360 ddGroup: this.drop_group || 'default_card_drag_group'
2362 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2363 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2364 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2365 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2366 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369 if (this.collapsable) {
2370 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2372 if (this.rotateable) {
2373 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2375 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2377 this.footerEl = this.el.select('.card-footer',true).first();
2378 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2379 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2380 this.headerEl = this.el.select('.card-header',true).first();
2383 this.el.addClass('roo-card-rotated');
2384 this.fireEvent('rotate', this, true);
2386 this.header_imageEl = this.el.select('.card-img-top',true).first();
2387 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390 getDragData : function(e)
2392 var target = this.getEl();
2394 //this.handleSelection(e);
2399 nodes: this.getEl(),
2404 dragData.ddel = target.dom ; // the div element
2405 Roo.log(target.getWidth( ));
2406 dragData.ddel.style.width = target.getWidth() + 'px';
2413 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2414 * whole Element becomes the target, and this causes the drop gesture to append.
2416 * Returns an object:
2419 position : 'below' or 'above'
2420 card : relateive to card OBJECT (or true for no cards listed)
2421 items_n : relative to nth item in list
2422 card_n : relative to nth card in list
2427 getTargetFromEvent : function(e, dragged_card_el)
2429 var target = e.getTarget();
2430 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2431 target = target.parentNode;
2442 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2443 // see if target is one of the 'cards'...
2446 //Roo.log(this.items.length);
2449 var last_card_n = 0;
2451 for (var i = 0;i< this.items.length;i++) {
2453 if (!this.items[i].el.hasClass('card')) {
2456 pos = this.getDropPoint(e, this.items[i].el.dom);
2458 cards_len = ret.cards.length;
2459 //Roo.log(this.items[i].el.dom.id);
2460 ret.cards.push(this.items[i]);
2462 if (ret.card_n < 0 && pos == 'above') {
2463 ret.position = cards_len > 0 ? 'below' : pos;
2464 ret.items_n = i > 0 ? i - 1 : 0;
2465 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2466 ret.card = ret.cards[ret.card_n];
2469 if (!ret.cards.length) {
2471 ret.position = 'below';
2475 // could not find a card.. stick it at the end..
2476 if (ret.card_n < 0) {
2477 ret.card_n = last_card_n;
2478 ret.card = ret.cards[last_card_n];
2479 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2480 ret.position = 'below';
2483 if (this.items[ret.items_n].el == dragged_card_el) {
2487 if (ret.position == 'below') {
2488 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2490 if (card_after && card_after.el == dragged_card_el) {
2497 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2499 if (card_before && card_before.el == dragged_card_el) {
2506 onNodeEnter : function(n, dd, e, data){
2509 onNodeOver : function(n, dd, e, data)
2512 var target_info = this.getTargetFromEvent(e,data.source.el);
2513 if (target_info === false) {
2514 this.dropPlaceHolder('hide');
2517 Roo.log(['getTargetFromEvent', target_info ]);
2520 if (this.fireEvent('cardover', this, [ data ]) === false) {
2524 this.dropPlaceHolder('show', target_info,data);
2528 onNodeOut : function(n, dd, e, data){
2529 this.dropPlaceHolder('hide');
2532 onNodeDrop : function(n, dd, e, data)
2535 // call drop - return false if
2537 // this could actually fail - if the Network drops..
2538 // we will ignore this at present..- client should probably reload
2539 // the whole set of cards if stuff like that fails.
2542 var info = this.getTargetFromEvent(e,data.source.el);
2543 if (info === false) {
2546 this.dropPlaceHolder('hide');
2550 this.acceptCard(data.source, info.position, info.card, info.items_n);
2554 firstChildCard : function()
2556 for (var i = 0;i< this.items.length;i++) {
2558 if (!this.items[i].el.hasClass('card')) {
2561 return this.items[i];
2563 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2568 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2570 acceptCard : function(move_card, position, next_to_card )
2572 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2576 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2578 move_card.parent().removeCard(move_card);
2581 var dom = move_card.el.dom;
2582 dom.style.width = ''; // clear with - which is set by drag.
2584 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2585 var cardel = next_to_card.el.dom;
2587 if (position == 'above' ) {
2588 cardel.parentNode.insertBefore(dom, cardel);
2589 } else if (cardel.nextSibling) {
2590 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2592 cardel.parentNode.append(dom);
2595 // card container???
2596 this.containerEl.dom.append(dom);
2599 //FIXME HANDLE card = true
2601 // add this to the correct place in items.
2603 // remove Card from items.
2606 if (this.items.length) {
2608 //Roo.log([info.items_n, info.position, this.items.length]);
2609 for (var i =0; i < this.items.length; i++) {
2610 if (i == to_items_n && position == 'above') {
2611 nitems.push(move_card);
2613 nitems.push(this.items[i]);
2614 if (i == to_items_n && position == 'below') {
2615 nitems.push(move_card);
2618 this.items = nitems;
2619 Roo.log(this.items);
2621 this.items.push(move_card);
2624 move_card.parentId = this.id;
2630 removeCard : function(c)
2632 this.items = this.items.filter(function(e) { return e != c });
2635 dom.parentNode.removeChild(dom);
2636 dom.style.width = ''; // clear with - which is set by drag.
2641 /** Decide whether to drop above or below a View node. */
2642 getDropPoint : function(e, n, dd)
2647 if (n == this.containerEl.dom) {
2650 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2651 var c = t + (b - t) / 2;
2652 var y = Roo.lib.Event.getPageY(e);
2659 onToggleCollapse : function(e)
2661 if (this.collapsed) {
2662 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2663 this.collapsableEl.addClass('show');
2664 this.collapsed = false;
2667 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2668 this.collapsableEl.removeClass('show');
2669 this.collapsed = true;
2674 onToggleRotate : function(e)
2676 this.collapsableEl.removeClass('show');
2677 this.footerEl.removeClass('d-none');
2678 this.el.removeClass('roo-card-rotated');
2679 this.el.removeClass('d-none');
2682 this.collapsableEl.addClass('show');
2683 this.rotated = false;
2684 this.fireEvent('rotate', this, this.rotated);
2687 this.el.addClass('roo-card-rotated');
2688 this.footerEl.addClass('d-none');
2689 this.el.select('.roo-collapsable').removeClass('show');
2691 this.rotated = true;
2692 this.fireEvent('rotate', this, this.rotated);
2696 dropPlaceHolder: function (action, info, data)
2698 if (this.dropEl === false) {
2699 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2703 this.dropEl.removeClass(['d-none', 'd-block']);
2704 if (action == 'hide') {
2706 this.dropEl.addClass('d-none');
2709 // FIXME - info.card == true!!!
2710 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2712 if (info.card !== true) {
2713 var cardel = info.card.el.dom;
2715 if (info.position == 'above') {
2716 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2717 } else if (cardel.nextSibling) {
2718 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2720 cardel.parentNode.append(this.dropEl.dom);
2723 // card container???
2724 this.containerEl.dom.append(this.dropEl.dom);
2727 this.dropEl.addClass('d-block roo-card-dropzone');
2729 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2736 setHeaderText: function(html)
2739 if (this.headerContainerEl) {
2740 this.headerContainerEl.dom.innerHTML = html;
2743 onHeaderImageLoad : function(ev, he)
2745 if (!this.header_image_fit_square) {
2749 var hw = he.naturalHeight / he.naturalWidth;
2752 //var w = he.dom.naturalWidth;
2755 he.style.position = 'relative';
2757 var nw = (ww * (1/hw));
2758 Roo.get(he).setSize( ww * (1/hw), ww);
2759 he.style.left = ((ww - nw)/ 2) + 'px';
2760 he.style.position = 'relative';
2771 * Card header - holder for the card header elements.
2776 * @class Roo.bootstrap.CardHeader
2777 * @extends Roo.bootstrap.Element
2778 * @parent Roo.bootstrap.Card
2779 * @children Roo.bootstrap.Component
2780 * Bootstrap CardHeader class
2782 * Create a new Card Header - that you can embed children into
2783 * @param {Object} config The config object
2786 Roo.bootstrap.CardHeader = function(config){
2787 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2793 container_method : 'getCardHeader'
2806 * Card footer - holder for the card footer elements.
2811 * @class Roo.bootstrap.CardFooter
2812 * @extends Roo.bootstrap.Element
2813 * @parent Roo.bootstrap.Card
2814 * @children Roo.bootstrap.Component
2815 * Bootstrap CardFooter class
2818 * Create a new Card Footer - that you can embed children into
2819 * @param {Object} config The config object
2822 Roo.bootstrap.CardFooter = function(config){
2823 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2829 container_method : 'getCardFooter'
2842 * Card header - holder for the card header elements.
2847 * @class Roo.bootstrap.CardImageTop
2848 * @extends Roo.bootstrap.Element
2849 * @parent Roo.bootstrap.Card
2850 * @children Roo.bootstrap.Component
2851 * Bootstrap CardImageTop class
2854 * Create a new Card Image Top container
2855 * @param {Object} config The config object
2858 Roo.bootstrap.CardImageTop = function(config){
2859 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2865 container_method : 'getCardImageTop'
2880 * @class Roo.bootstrap.ButtonUploader
2881 * @extends Roo.bootstrap.Button
2882 * Bootstrap Button Uploader class - it's a button which when you add files to it
2885 * @cfg {Number} errorTimeout default 3000
2886 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2887 * @cfg {Array} html The button text.
2888 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2891 * Create a new CardUploader
2892 * @param {Object} config The config object
2895 Roo.bootstrap.ButtonUploader = function(config){
2899 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2905 * @event beforeselect
2906 * When button is pressed, before show upload files dialog is shown
2907 * @param {Roo.bootstrap.UploaderButton} this
2910 'beforeselect' : true,
2912 * @event fired when files have been selected,
2913 * When a the download link is clicked
2914 * @param {Roo.bootstrap.UploaderButton} this
2915 * @param {Array} Array of files that have been uploaded
2922 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2925 errorTimeout : 3000,
2929 fileCollection : false,
2934 getAutoCreate : function()
2939 cls : 'd-none roo-card-upload-selector'
2942 if (this.multiple) {
2943 im.multiple = 'multiple';
2949 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2959 initEvents : function()
2962 Roo.bootstrap.Button.prototype.initEvents.call(this);
2968 this.urlAPI = (window.createObjectURL && window) ||
2969 (window.URL && URL.revokeObjectURL && URL) ||
2970 (window.webkitURL && webkitURL);
2975 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2977 this.selectorEl.on('change', this.onFileSelected, this);
2984 onClick : function(e)
2988 if ( this.fireEvent('beforeselect', this) === false) {
2992 this.selectorEl.dom.click();
2996 onFileSelected : function(e)
3000 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004 this.selectorEl.dom.value = '';// hopefully reset..
3006 this.fireEvent('uploaded', this, files );
3014 * addCard - add an Attachment to the uploader
3015 * @param data - the data about the image to upload
3019 title : "Title of file",
3020 is_uploaded : false,
3021 src : "http://.....",
3022 srcfile : { the File upload object },
3023 mimetype : file.type,
3026 .. any other data...
3051 * @class Roo.bootstrap.Img
3052 * @extends Roo.bootstrap.Component
3053 * Bootstrap Img class
3054 * @cfg {Boolean} imgResponsive false | true
3055 * @cfg {String} border rounded | circle | thumbnail
3056 * @cfg {String} src image source
3057 * @cfg {String} alt image alternative text
3058 * @cfg {String} href a tag href
3059 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060 * @cfg {String} xsUrl xs image source
3061 * @cfg {String} smUrl sm image source
3062 * @cfg {String} mdUrl md image source
3063 * @cfg {String} lgUrl lg image source
3064 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067 * Create a new Input
3068 * @param {Object} config The config object
3071 Roo.bootstrap.Img = function(config){
3072 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3078 * The img click event for the img.
3079 * @param {Roo.EventObject} e
3084 * The when any image loads
3085 * @param {Roo.EventObject} e
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3093 imgResponsive: true,
3102 backgroundContain : false,
3104 getAutoCreate : function()
3106 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107 return this.createSingleImg();
3112 cls: 'roo-image-responsive-group',
3117 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3119 if(!_this[size + 'Url']){
3125 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126 html: _this.html || cfg.html,
3127 src: _this[size + 'Url']
3130 img.cls += ' roo-image-responsive-' + size;
3132 var s = ['xs', 'sm', 'md', 'lg'];
3134 s.splice(s.indexOf(size), 1);
3136 Roo.each(s, function(ss){
3137 img.cls += ' hidden-' + ss;
3140 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141 cfg.cls += ' img-' + _this.border;
3145 cfg.alt = _this.alt;
3158 a.target = _this.target;
3162 cfg.cn.push((_this.href) ? a : img);
3169 createSingleImg : function()
3173 cls: (this.imgResponsive) ? 'img-responsive' : '',
3175 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3178 if (this.backgroundContain) {
3179 cfg.cls += ' background-contain';
3182 cfg.html = this.html || cfg.html;
3184 if (this.backgroundContain) {
3185 cfg.style="background-image: url(" + this.src + ')';
3187 cfg.src = this.src || cfg.src;
3190 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191 cfg.cls += ' img-' + this.border;
3208 a.target = this.target;
3213 return (this.href) ? a : cfg;
3216 initEvents: function()
3219 this.el.on('click', this.onClick, this);
3221 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222 this.el.on('load', this.onImageLoad, this);
3224 // not sure if this works.. not tested
3225 this.el.select('img', true).on('load', this.onImageLoad, this);
3230 onClick : function(e)
3232 Roo.log('img onclick');
3233 this.fireEvent('click', this, e);
3235 onImageLoad: function(e)
3237 Roo.log('img load');
3238 this.fireEvent('load', this, e);
3242 * Sets the url of the image - used to update it
3243 * @param {String} url the url of the image
3246 setSrc : function(url)
3250 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251 if (this.backgroundContain) {
3252 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3254 this.el.dom.src = url;
3259 this.el.select('img', true).first().dom.src = url;
3275 * @class Roo.bootstrap.Link
3276 * @extends Roo.bootstrap.Component
3277 * @children Roo.bootstrap.Component
3278 * Bootstrap Link Class (eg. '<a href>')
3280 * @cfg {String} alt image alternative text
3281 * @cfg {String} href a tag href
3282 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283 * @cfg {String} html the content of the link.
3284 * @cfg {String} anchor name for the anchor link
3285 * @cfg {String} fa - favicon
3287 * @cfg {Boolean} preventDefault (true | false) default false
3291 * Create a new Input
3292 * @param {Object} config The config object
3295 Roo.bootstrap.Link = function(config){
3296 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3302 * The img click event for the img.
3303 * @param {Roo.EventObject} e
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3313 preventDefault: false,
3319 getAutoCreate : function()
3321 var html = this.html || '';
3323 if (this.fa !== false) {
3324 html = '<i class="fa fa-' + this.fa + '"></i>';
3329 // anchor's do not require html/href...
3330 if (this.anchor === false) {
3332 cfg.href = this.href || '#';
3334 cfg.name = this.anchor;
3335 if (this.html !== false || this.fa !== false) {
3338 if (this.href !== false) {
3339 cfg.href = this.href;
3343 if(this.alt !== false){
3348 if(this.target !== false) {
3349 cfg.target = this.target;
3355 initEvents: function() {
3357 if(!this.href || this.preventDefault){
3358 this.el.on('click', this.onClick, this);
3362 onClick : function(e)
3364 if(this.preventDefault){
3367 //Roo.log('img onclick');
3368 this.fireEvent('click', this, e);
3381 * @class Roo.bootstrap.Header
3382 * @extends Roo.bootstrap.Component
3383 * @children Roo.bootstrap.Component
3384 * Bootstrap Header class
3387 * @cfg {String} html content of header
3388 * @cfg {Number} level (1|2|3|4|5|6) default 1
3391 * Create a new Header
3392 * @param {Object} config The config object
3396 Roo.bootstrap.Header = function(config){
3397 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3408 getAutoCreate : function(){
3413 tag: 'h' + (1 *this.level),
3414 html: this.html || ''
3424 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
3426 * @class Roo.bootstrap.MenuMgr
3428 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3431 Roo.bootstrap.menu.Manager = function(){
3432 var menus, active, groups = {}, attached = false, lastShow = new Date();
3434 // private - called when first menu is created
3437 active = new Roo.util.MixedCollection();
3438 Roo.get(document).addKeyListener(27, function(){
3439 if(active.length > 0){
3447 if(active && active.length > 0){
3448 var c = active.clone();
3458 if(active.length < 1){
3459 Roo.get(document).un("mouseup", onMouseDown);
3467 var last = active.last();
3468 lastShow = new Date();
3471 Roo.get(document).on("mouseup", onMouseDown);
3476 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3477 m.parentMenu.activeChild = m;
3478 }else if(last && last.isVisible()){
3479 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3484 function onBeforeHide(m){
3486 m.activeChild.hide();
3488 if(m.autoHideTimer){
3489 clearTimeout(m.autoHideTimer);
3490 delete m.autoHideTimer;
3495 function onBeforeShow(m){
3496 var pm = m.parentMenu;
3497 if(!pm && !m.allowOtherMenus){
3499 }else if(pm && pm.activeChild && active != m){
3500 pm.activeChild.hide();
3504 // private this should really trigger on mouseup..
3505 function onMouseDown(e){
3506 Roo.log("on Mouse Up");
3508 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3509 Roo.log("MenuManager hideAll");
3518 function onBeforeCheck(mi, state){
3520 var g = groups[mi.group];
3521 for(var i = 0, l = g.length; i < l; i++){
3523 g[i].setChecked(false);
3532 * Hides all menus that are currently visible
3534 hideAll : function(){
3539 register : function(menu){
3543 menus[menu.id] = menu;
3544 menu.on("beforehide", onBeforeHide);
3545 menu.on("hide", onHide);
3546 menu.on("beforeshow", onBeforeShow);
3547 menu.on("show", onShow);
3549 if(g && menu.events["checkchange"]){
3553 groups[g].push(menu);
3554 menu.on("checkchange", onCheck);
3559 * Returns a {@link Roo.menu.Menu} object
3560 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3561 * be used to generate and return a new Menu instance.
3563 get : function(menu){
3564 if(typeof menu == "string"){ // menu id
3566 }else if(menu.events){ // menu instance
3569 /*else if(typeof menu.length == 'number'){ // array of menu items?
3570 return new Roo.bootstrap.Menu({items:menu});
3571 }else{ // otherwise, must be a config
3572 return new Roo.bootstrap.Menu(menu);
3579 unregister : function(menu){
3580 delete menus[menu.id];
3581 menu.un("beforehide", onBeforeHide);
3582 menu.un("hide", onHide);
3583 menu.un("beforeshow", onBeforeShow);
3584 menu.un("show", onShow);
3586 if(g && menu.events["checkchange"]){
3587 groups[g].remove(menu);
3588 menu.un("checkchange", onCheck);
3593 registerCheckable : function(menuItem){
3594 var g = menuItem.group;
3599 groups[g].push(menuItem);
3600 menuItem.on("beforecheckchange", onBeforeCheck);
3605 unregisterCheckable : function(menuItem){
3606 var g = menuItem.group;
3608 groups[g].remove(menuItem);
3609 menuItem.un("beforecheckchange", onBeforeCheck);
3615 * @class Roo.bootstrap.menu.Menu
3616 * @extends Roo.bootstrap.Component
3618 * @children Roo.bootstrap.menu.Item
3619 * Bootstrap Menu class - container for MenuItems
3621 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622 * @cfg {bool} hidden if the menu should be hidden when rendered.
3623 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3624 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3626 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3630 * @param {Object} config The config objectQ
3634 Roo.bootstrap.menu.Menu = function(config){
3636 if (config.type == 'treeview') {
3637 // normally menu's are drawn attached to the document to handle layering etc..
3638 // however treeview (used by the docs menu is drawn into the parent element)
3639 this.container_method = 'getChildContainer';
3642 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643 if (this.registerMenu && this.type != 'treeview') {
3644 Roo.bootstrap.menu.Manager.register(this);
3651 * Fires before this menu is displayed (return false to block)
3652 * @param {Roo.menu.Menu} this
3657 * Fires before this menu is hidden (return false to block)
3658 * @param {Roo.menu.Menu} this
3663 * Fires after this menu is displayed
3664 * @param {Roo.menu.Menu} this
3669 * Fires after this menu is hidden
3670 * @param {Roo.menu.Menu} this
3675 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676 * @param {Roo.menu.Menu} this
3677 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678 * @param {Roo.EventObject} e
3683 * Fires when the mouse is hovering over this menu
3684 * @param {Roo.menu.Menu} this
3685 * @param {Roo.EventObject} e
3686 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3691 * Fires when the mouse exits this menu
3692 * @param {Roo.menu.Menu} this
3693 * @param {Roo.EventObject} e
3694 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3699 * Fires when a menu item contained in this menu is clicked
3700 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701 * @param {Roo.EventObject} e
3705 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3712 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3715 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3717 registerMenu : true,
3719 menuItems :false, // stores the menu items..
3729 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3731 hideTrigger : false,
3736 getChildContainer : function() {
3740 getAutoCreate : function(){
3742 //if (['right'].indexOf(this.align)!==-1) {
3743 // cfg.cn[1].cls += ' pull-right'
3748 cls : 'dropdown-menu shadow' ,
3749 style : 'z-index:1000'
3753 if (this.type === 'submenu') {
3754 cfg.cls = 'submenu active';
3756 if (this.type === 'treeview') {
3757 cfg.cls = 'treeview-menu';
3762 initEvents : function() {
3764 // Roo.log("ADD event");
3765 // Roo.log(this.triggerEl.dom);
3766 if (this.triggerEl) {
3768 this.triggerEl.on('click', this.onTriggerClick, this);
3770 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3772 if (!this.hideTrigger) {
3773 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774 // dropdown toggle on the 'a' in BS4?
3775 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3777 this.triggerEl.addClass('dropdown-toggle');
3783 this.el.on('touchstart' , this.onTouch, this);
3785 this.el.on('click' , this.onClick, this);
3787 this.el.on("mouseover", this.onMouseOver, this);
3788 this.el.on("mouseout", this.onMouseOut, this);
3792 findTargetItem : function(e)
3794 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3798 //Roo.log(t); Roo.log(t.id);
3800 //Roo.log(this.menuitems);
3801 return this.menuitems.get(t.id);
3803 //return this.items.get(t.menuItemId);
3809 onTouch : function(e)
3811 Roo.log("menu.onTouch");
3812 //e.stopEvent(); this make the user popdown broken
3816 onClick : function(e)
3818 Roo.log("menu.onClick");
3820 var t = this.findTargetItem(e);
3821 if(!t || t.isContainer){
3826 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3827 if(t == this.activeItem && t.shouldDeactivate(e)){
3828 this.activeItem.deactivate();
3829 delete this.activeItem;
3833 this.setActiveItem(t, true);
3841 Roo.log('pass click event');
3845 this.fireEvent("click", this, t, e);
3849 if(!t.href.length || t.href == '#'){
3850 (function() { _this.hide(); }).defer(100);
3855 onMouseOver : function(e){
3856 var t = this.findTargetItem(e);
3859 // if(t.canActivate && !t.disabled){
3860 // this.setActiveItem(t, true);
3864 this.fireEvent("mouseover", this, e, t);
3866 isVisible : function(){
3867 return !this.hidden;
3869 onMouseOut : function(e){
3870 var t = this.findTargetItem(e);
3873 // if(t == this.activeItem && t.shouldDeactivate(e)){
3874 // this.activeItem.deactivate();
3875 // delete this.activeItem;
3878 this.fireEvent("mouseout", this, e, t);
3883 * Displays this menu relative to another element
3884 * @param {String/HTMLElement/Roo.Element} element The element to align to
3885 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886 * the element (defaults to this.defaultAlign)
3887 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3889 show : function(el, pos, parentMenu)
3891 if (false === this.fireEvent("beforeshow", this)) {
3892 Roo.log("show canceled");
3895 this.parentMenu = parentMenu;
3899 this.el.addClass('show'); // show otherwise we do not know how big we are..
3901 var xy = this.el.getAlignToXY(el, pos);
3903 // bl-tl << left align below
3904 // tl-bl << left align
3906 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907 // if it goes to far to the right.. -> align left.
3908 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911 // was left align - go right?
3912 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915 // goes down the bottom
3916 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3918 var a = this.align.replace('?', '').split('-');
3919 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3923 this.showAt( xy , parentMenu, false);
3926 * Displays this menu at a specific xy position
3927 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3930 showAt : function(xy, parentMenu, /* private: */_e){
3931 this.parentMenu = parentMenu;
3936 this.fireEvent("beforeshow", this);
3937 //xy = this.el.adjustForConstraints(xy);
3941 this.hideMenuItems();
3942 this.hidden = false;
3943 if (this.triggerEl) {
3944 this.triggerEl.addClass('open');
3947 this.el.addClass('show');
3951 // reassign x when hitting right
3953 // reassign y when hitting bottom
3955 // but the list may align on trigger left or trigger top... should it be a properity?
3957 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3962 this.fireEvent("show", this);
3968 this.doFocus.defer(50, this);
3972 doFocus : function(){
3974 this.focusEl.focus();
3979 * Hides this menu and optionally all parent menus
3980 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3982 hide : function(deep)
3984 if (false === this.fireEvent("beforehide", this)) {
3985 Roo.log("hide canceled");
3988 this.hideMenuItems();
3989 if(this.el && this.isVisible()){
3991 if(this.activeItem){
3992 this.activeItem.deactivate();
3993 this.activeItem = null;
3995 if (this.triggerEl) {
3996 this.triggerEl.removeClass('open');
3999 this.el.removeClass('show');
4001 this.fireEvent("hide", this);
4003 if(deep === true && this.parentMenu){
4004 this.parentMenu.hide(true);
4008 onTriggerClick : function(e)
4010 Roo.log('trigger click');
4012 var target = e.getTarget();
4014 Roo.log(target.nodeName.toLowerCase());
4016 if(target.nodeName.toLowerCase() === 'i'){
4022 onTriggerPress : function(e)
4024 Roo.log('trigger press');
4025 //Roo.log(e.getTarget());
4026 // Roo.log(this.triggerEl.dom);
4028 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029 var pel = Roo.get(e.getTarget());
4030 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031 Roo.log('is treeview or dropdown?');
4035 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4039 if (this.isVisible()) {
4045 this.show(this.triggerEl, this.align, false);
4048 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4055 hideMenuItems : function()
4057 Roo.log("hide Menu Items");
4062 this.el.select('.open',true).each(function(aa) {
4064 aa.removeClass('open');
4068 addxtypeChild : function (tree, cntr) {
4069 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4071 this.menuitems.add(comp);
4083 this.getEl().dom.innerHTML = '';
4084 this.menuitems.clear();
4090 * @class Roo.bootstrap.menu.Item
4091 * @extends Roo.bootstrap.Component
4092 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4094 * Bootstrap MenuItem class
4096 * @cfg {String} html the menu label
4097 * @cfg {String} href the link
4098 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4099 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4100 * @cfg {Boolean} active used on sidebars to highlight active itesm
4101 * @cfg {String} fa favicon to show on left of menu item.
4102 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4106 * Create a new MenuItem
4107 * @param {Object} config The config object
4111 Roo.bootstrap.menu.Item = function(config){
4112 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4117 * The raw click event for the entire grid.
4118 * @param {Roo.bootstrap.menu.Item} this
4119 * @param {Roo.EventObject} e
4125 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4129 preventDefault: false,
4130 isContainer : false,
4134 getAutoCreate : function(){
4136 if(this.isContainer){
4139 cls: 'dropdown-menu-item '
4149 cls : 'dropdown-item',
4154 if (this.fa !== false) {
4157 cls : 'fa fa-' + this.fa
4166 cls: 'dropdown-menu-item',
4169 if (this.parent().type == 'treeview') {
4170 cfg.cls = 'treeview-menu';
4173 cfg.cls += ' active';
4178 anc.href = this.href || cfg.cn[0].href ;
4179 ctag.html = this.html || cfg.cn[0].html ;
4183 initEvents: function()
4185 if (this.parent().type == 'treeview') {
4186 this.el.select('a').on('click', this.onClick, this);
4190 this.menu.parentType = this.xtype;
4191 this.menu.triggerEl = this.el;
4192 this.menu = this.addxtype(Roo.apply({}, this.menu));
4196 onClick : function(e)
4198 Roo.log('item on click ');
4200 if(this.preventDefault){
4203 //this.parent().hideMenuItems();
4205 this.fireEvent('click', this, e);
4219 * @class Roo.bootstrap.menu.Separator
4220 * @extends Roo.bootstrap.Component
4222 * Bootstrap Separator class
4225 * Create a new Separator
4226 * @param {Object} config The config object
4230 Roo.bootstrap.menu.Separator = function(config){
4231 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4234 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4236 getAutoCreate : function(){
4239 cls: 'dropdown-divider divider'
4250 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
4251 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
4252 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
4260 * @class Roo.bootstrap.Modal
4261 * @extends Roo.bootstrap.Component
4264 * @children Roo.bootstrap.Component
4265 * Bootstrap Modal class
4266 * @cfg {String} title Title of dialog
4267 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4268 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4269 * @cfg {Boolean} specificTitle default false
4270 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4271 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4272 * @cfg {Boolean} animate default true
4273 * @cfg {Boolean} allow_close default true
4274 * @cfg {Boolean} fitwindow default false
4275 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4276 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4277 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4278 * @cfg {String} size (sm|lg|xl) default empty
4279 * @cfg {Number} max_width set the max width of modal
4280 * @cfg {Boolean} editableTitle can the title be edited
4285 * Create a new Modal Dialog
4286 * @param {Object} config The config object
4289 Roo.bootstrap.Modal = function(config){
4290 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4295 * The raw btnclick event for the button
4296 * @param {Roo.EventObject} e
4301 * Fire when dialog resize
4302 * @param {Roo.bootstrap.Modal} this
4303 * @param {Roo.EventObject} e
4307 * @event titlechanged
4308 * Fire when the editable title has been changed
4309 * @param {Roo.bootstrap.Modal} this
4310 * @param {Roo.EventObject} value
4312 "titlechanged" : true
4315 this.buttons = this.buttons || [];
4318 this.tmpl = Roo.factory(this.tmpl);
4323 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4325 title : 'test dialog',
4335 specificTitle: false,
4337 buttonPosition: 'right',
4359 editableTitle : false,
4361 onRender : function(ct, position)
4363 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4366 var cfg = Roo.apply({}, this.getAutoCreate());
4369 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4371 //if (!cfg.name.length) {
4375 cfg.cls += ' ' + this.cls;
4378 cfg.style = this.style;
4380 this.el = Roo.get(document.body).createChild(cfg, position);
4382 //var type = this.el.dom.type;
4385 if(this.tabIndex !== undefined){
4386 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4389 this.dialogEl = this.el.select('.modal-dialog',true).first();
4390 this.bodyEl = this.el.select('.modal-body',true).first();
4391 this.closeEl = this.el.select('.modal-header .close', true).first();
4392 this.headerEl = this.el.select('.modal-header',true).first();
4393 this.titleEl = this.el.select('.modal-title',true).first();
4394 this.footerEl = this.el.select('.modal-footer',true).first();
4396 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4398 //this.el.addClass("x-dlg-modal");
4400 if (this.buttons.length) {
4401 Roo.each(this.buttons, function(bb) {
4402 var b = Roo.apply({}, bb);
4403 b.xns = b.xns || Roo.bootstrap;
4404 b.xtype = b.xtype || 'Button';
4405 if (typeof(b.listeners) == 'undefined') {
4406 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4409 var btn = Roo.factory(b);
4411 btn.render(this.getButtonContainer());
4415 // render the children.
4418 if(typeof(this.items) != 'undefined'){
4419 var items = this.items;
4422 for(var i =0;i < items.length;i++) {
4423 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4427 this.items = nitems;
4429 // where are these used - they used to be body/close/footer
4433 //this.el.addClass([this.fieldClass, this.cls]);
4437 getAutoCreate : function()
4439 // we will default to modal-body-overflow - might need to remove or make optional later.
4441 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4442 html : this.html || ''
4447 cls : 'modal-title',
4451 if(this.specificTitle){ // WTF is this?
4456 if (this.allow_close && Roo.bootstrap.version == 3) {
4466 if (this.editableTitle) {
4468 cls: 'form-control roo-editable-title d-none',
4474 if (this.allow_close && Roo.bootstrap.version == 4) {
4484 if(this.size.length){
4485 size = 'modal-' + this.size;
4488 var footer = Roo.bootstrap.version == 3 ?
4490 cls : 'modal-footer',
4494 cls: 'btn-' + this.buttonPosition
4499 { // BS4 uses mr-auto on left buttons....
4500 cls : 'modal-footer'
4511 cls: "modal-dialog " + size,
4514 cls : "modal-content",
4517 cls : 'modal-header',
4532 modal.cls += ' fade';
4538 getChildContainer : function() {
4543 getButtonContainer : function() {
4545 return Roo.bootstrap.version == 4 ?
4546 this.el.select('.modal-footer',true).first()
4547 : this.el.select('.modal-footer div',true).first();
4550 initEvents : function()
4552 if (this.allow_close) {
4553 this.closeEl.on('click', this.hide, this);
4555 Roo.EventManager.onWindowResize(this.resize, this, true);
4556 if (this.editableTitle) {
4557 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4558 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4559 this.headerEditEl.on('keyup', function(e) {
4560 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4561 this.toggleHeaderInput(false)
4564 this.headerEditEl.on('blur', function(e) {
4565 this.toggleHeaderInput(false)
4574 this.maskEl.setSize(
4575 Roo.lib.Dom.getViewWidth(true),
4576 Roo.lib.Dom.getViewHeight(true)
4579 if (this.fitwindow) {
4581 this.dialogEl.setStyle( { 'max-width' : '100%' });
4583 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4584 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4589 if(this.max_width !== 0) {
4591 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4594 this.setSize(w, this.height);
4598 if(this.max_height) {
4599 this.setSize(w,Math.min(
4601 Roo.lib.Dom.getViewportHeight(true) - 60
4607 if(!this.fit_content) {
4608 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4612 this.setSize(w, Math.min(
4614 this.headerEl.getHeight() +
4615 this.footerEl.getHeight() +
4616 this.getChildHeight(this.bodyEl.dom.childNodes),
4617 Roo.lib.Dom.getViewportHeight(true) - 60)
4623 setSize : function(w,h)
4634 if (!this.rendered) {
4637 this.toggleHeaderInput(false);
4638 //this.el.setStyle('display', 'block');
4639 this.el.removeClass('hideing');
4640 this.el.dom.style.display='block';
4642 Roo.get(document.body).addClass('modal-open');
4644 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4647 this.el.addClass('show');
4648 this.el.addClass('in');
4651 this.el.addClass('show');
4652 this.el.addClass('in');
4655 // not sure how we can show data in here..
4657 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4660 Roo.get(document.body).addClass("x-body-masked");
4662 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4663 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664 this.maskEl.dom.style.display = 'block';
4665 this.maskEl.addClass('show');
4670 this.fireEvent('show', this);
4672 // set zindex here - otherwise it appears to be ignored...
4673 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4676 this.items.forEach( function(e) {
4677 e.layout ? e.layout() : false;
4685 if(this.fireEvent("beforehide", this) !== false){
4687 this.maskEl.removeClass('show');
4689 this.maskEl.dom.style.display = '';
4690 Roo.get(document.body).removeClass("x-body-masked");
4691 this.el.removeClass('in');
4692 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4694 if(this.animate){ // why
4695 this.el.addClass('hideing');
4696 this.el.removeClass('show');
4698 if (!this.el.hasClass('hideing')) {
4699 return; // it's been shown again...
4702 this.el.dom.style.display='';
4704 Roo.get(document.body).removeClass('modal-open');
4705 this.el.removeClass('hideing');
4709 this.el.removeClass('show');
4710 this.el.dom.style.display='';
4711 Roo.get(document.body).removeClass('modal-open');
4714 this.fireEvent('hide', this);
4717 isVisible : function()
4720 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4724 addButton : function(str, cb)
4728 var b = Roo.apply({}, { html : str } );
4729 b.xns = b.xns || Roo.bootstrap;
4730 b.xtype = b.xtype || 'Button';
4731 if (typeof(b.listeners) == 'undefined') {
4732 b.listeners = { click : cb.createDelegate(this) };
4735 var btn = Roo.factory(b);
4737 btn.render(this.getButtonContainer());
4743 setDefaultButton : function(btn)
4745 //this.el.select('.modal-footer').()
4748 resizeTo: function(w,h)
4750 this.dialogEl.setWidth(w);
4752 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4754 this.bodyEl.setHeight(h - diff);
4756 this.fireEvent('resize', this);
4759 setContentSize : function(w, h)
4763 onButtonClick: function(btn,e)
4766 this.fireEvent('btnclick', btn.name, e);
4769 * Set the title of the Dialog
4770 * @param {String} str new Title
4772 setTitle: function(str) {
4773 this.titleEl.dom.innerHTML = str;
4777 * Set the body of the Dialog
4778 * @param {String} str new Title
4780 setBody: function(str) {
4781 this.bodyEl.dom.innerHTML = str;
4784 * Set the body of the Dialog using the template
4785 * @param {Obj} data - apply this data to the template and replace the body contents.
4787 applyBody: function(obj)
4790 Roo.log("Error - using apply Body without a template");
4793 this.tmpl.overwrite(this.bodyEl, obj);
4796 getChildHeight : function(child_nodes)
4800 child_nodes.length == 0
4805 var child_height = 0;
4807 for(var i = 0; i < child_nodes.length; i++) {
4810 * for modal with tabs...
4811 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4813 var layout_childs = child_nodes[i].childNodes;
4815 for(var j = 0; j < layout_childs.length; j++) {
4817 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4819 var layout_body_childs = layout_childs[j].childNodes;
4821 for(var k = 0; k < layout_body_childs.length; k++) {
4823 if(layout_body_childs[k].classList.contains('navbar')) {
4824 child_height += layout_body_childs[k].offsetHeight;
4828 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4830 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4832 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4834 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4835 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4850 child_height += child_nodes[i].offsetHeight;
4851 // Roo.log(child_nodes[i].offsetHeight);
4854 return child_height;
4856 toggleHeaderInput : function(is_edit)
4858 if (!this.editableTitle) {
4859 return; // not editable.
4861 if (is_edit && this.is_header_editing) {
4862 return; // already editing..
4866 this.headerEditEl.dom.value = this.title;
4867 this.headerEditEl.removeClass('d-none');
4868 this.headerEditEl.dom.focus();
4869 this.titleEl.addClass('d-none');
4871 this.is_header_editing = true;
4874 // flip back to not editing.
4875 this.title = this.headerEditEl.dom.value;
4876 this.headerEditEl.addClass('d-none');
4877 this.titleEl.removeClass('d-none');
4878 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4879 this.is_header_editing = false;
4880 this.fireEvent('titlechanged', this, this.title);
4889 Roo.apply(Roo.bootstrap.Modal, {
4891 * Button config that displays a single OK button
4900 * Button config that displays Yes and No buttons
4916 * Button config that displays OK and Cancel buttons
4931 * Button config that displays Yes, No and Cancel buttons
4956 * messagebox - can be used as a replace
4960 * @class Roo.MessageBox
4961 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4965 Roo.Msg.alert('Status', 'Changes saved successfully.');
4967 // Prompt for user data:
4968 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4970 // process text value...
4974 // Show a dialog using config options:
4976 title:'Save Changes?',
4977 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4978 buttons: Roo.Msg.YESNOCANCEL,
4985 Roo.bootstrap.MessageBox = function(){
4986 var dlg, opt, mask, waitTimer;
4987 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4988 var buttons, activeTextEl, bwidth;
4992 var handleButton = function(button){
4994 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4998 var handleHide = function(){
5000 dlg.el.removeClass(opt.cls);
5003 // Roo.TaskMgr.stop(waitTimer);
5004 // waitTimer = null;
5009 var updateButtons = function(b){
5012 buttons["ok"].hide();
5013 buttons["cancel"].hide();
5014 buttons["yes"].hide();
5015 buttons["no"].hide();
5016 dlg.footerEl.hide();
5020 dlg.footerEl.show();
5021 for(var k in buttons){
5022 if(typeof buttons[k] != "function"){
5025 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5026 width += buttons[k].el.getWidth()+15;
5036 var handleEsc = function(d, k, e){
5037 if(opt && opt.closable !== false){
5047 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5048 * @return {Roo.BasicDialog} The BasicDialog element
5050 getDialog : function(){
5052 dlg = new Roo.bootstrap.Modal( {
5055 //constraintoviewport:false,
5057 //collapsible : false,
5062 //buttonAlign:"center",
5063 closeClick : function(){
5064 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5067 handleButton("cancel");
5072 dlg.on("hide", handleHide);
5074 //dlg.addKeyListener(27, handleEsc);
5076 this.buttons = buttons;
5077 var bt = this.buttonText;
5078 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5079 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5080 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5081 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5083 bodyEl = dlg.bodyEl.createChild({
5085 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5086 '<textarea class="roo-mb-textarea"></textarea>' +
5087 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5089 msgEl = bodyEl.dom.firstChild;
5090 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5091 textboxEl.enableDisplayMode();
5092 textboxEl.addKeyListener([10,13], function(){
5093 if(dlg.isVisible() && opt && opt.buttons){
5096 }else if(opt.buttons.yes){
5097 handleButton("yes");
5101 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5102 textareaEl.enableDisplayMode();
5103 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5104 progressEl.enableDisplayMode();
5106 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5107 var pf = progressEl.dom.firstChild;
5109 pp = Roo.get(pf.firstChild);
5110 pp.setHeight(pf.offsetHeight);
5118 * Updates the message box body text
5119 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5120 * the XHTML-compliant non-breaking space character '&#160;')
5121 * @return {Roo.MessageBox} This message box
5123 updateText : function(text)
5125 if(!dlg.isVisible() && !opt.width){
5126 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5127 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5129 msgEl.innerHTML = text || ' ';
5131 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5132 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5134 Math.min(opt.width || cw , this.maxWidth),
5135 Math.max(opt.minWidth || this.minWidth, bwidth)
5138 activeTextEl.setWidth(w);
5140 if(dlg.isVisible()){
5141 dlg.fixedcenter = false;
5143 // to big, make it scroll. = But as usual stupid IE does not support
5146 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5147 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5148 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5150 bodyEl.dom.style.height = '';
5151 bodyEl.dom.style.overflowY = '';
5154 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5156 bodyEl.dom.style.overflowX = '';
5159 dlg.setContentSize(w, bodyEl.getHeight());
5160 if(dlg.isVisible()){
5161 dlg.fixedcenter = true;
5167 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5168 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5169 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5170 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5171 * @return {Roo.MessageBox} This message box
5173 updateProgress : function(value, text){
5175 this.updateText(text);
5178 if (pp) { // weird bug on my firefox - for some reason this is not defined
5179 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5180 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5186 * Returns true if the message box is currently displayed
5187 * @return {Boolean} True if the message box is visible, else false
5189 isVisible : function(){
5190 return dlg && dlg.isVisible();
5194 * Hides the message box if it is displayed
5197 if(this.isVisible()){
5203 * Displays a new message box, or reinitializes an existing message box, based on the config options
5204 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5205 * The following config object properties are supported:
5207 Property Type Description
5208 ---------- --------------- ------------------------------------------------------------------------------------
5209 animEl String/Element An id or Element from which the message box should animate as it opens and
5210 closes (defaults to undefined)
5211 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5212 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5213 closable Boolean False to hide the top-right close button (defaults to true). Note that
5214 progress and wait dialogs will ignore this property and always hide the
5215 close button as they can only be closed programmatically.
5216 cls String A custom CSS class to apply to the message box element
5217 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5218 displayed (defaults to 75)
5219 fn Function A callback function to execute after closing the dialog. The arguments to the
5220 function will be btn (the name of the button that was clicked, if applicable,
5221 e.g. "ok"), and text (the value of the active text field, if applicable).
5222 Progress and wait dialogs will ignore this option since they do not respond to
5223 user actions and can only be closed programmatically, so any required function
5224 should be called by the same code after it closes the dialog.
5225 icon String A CSS class that provides a background image to be used as an icon for
5226 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5227 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5228 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5229 modal Boolean False to allow user interaction with the page while the message box is
5230 displayed (defaults to true)
5231 msg String A string that will replace the existing message box body text (defaults
5232 to the XHTML-compliant non-breaking space character ' ')
5233 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5234 progress Boolean True to display a progress bar (defaults to false)
5235 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5236 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5237 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5238 title String The title text
5239 value String The string value to set into the active textbox element if displayed
5240 wait Boolean True to display a progress bar (defaults to false)
5241 width Number The width of the dialog in pixels
5248 msg: 'Please enter your address:',
5250 buttons: Roo.MessageBox.OKCANCEL,
5253 animEl: 'addAddressBtn'
5256 * @param {Object} config Configuration options
5257 * @return {Roo.MessageBox} This message box
5259 show : function(options)
5262 // this causes nightmares if you show one dialog after another
5263 // especially on callbacks..
5265 if(this.isVisible()){
5268 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5269 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5270 Roo.log("New Dialog Message:" + options.msg )
5271 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5272 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5275 var d = this.getDialog();
5277 d.setTitle(opt.title || " ");
5278 d.closeEl.setDisplayed(opt.closable !== false);
5279 activeTextEl = textboxEl;
5280 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5285 textareaEl.setHeight(typeof opt.multiline == "number" ?
5286 opt.multiline : this.defaultTextHeight);
5287 activeTextEl = textareaEl;
5296 progressEl.setDisplayed(opt.progress === true);
5298 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5300 this.updateProgress(0);
5301 activeTextEl.dom.value = opt.value || "";
5303 dlg.setDefaultButton(activeTextEl);
5305 var bs = opt.buttons;
5309 }else if(bs && bs.yes){
5310 db = buttons["yes"];
5312 dlg.setDefaultButton(db);
5314 bwidth = updateButtons(opt.buttons);
5315 this.updateText(opt.msg);
5317 d.el.addClass(opt.cls);
5319 d.proxyDrag = opt.proxyDrag === true;
5320 d.modal = opt.modal !== false;
5321 d.mask = opt.modal !== false ? mask : false;
5323 // force it to the end of the z-index stack so it gets a cursor in FF
5324 document.body.appendChild(dlg.el.dom);
5325 d.animateTarget = null;
5326 d.show(options.animEl);
5332 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5333 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5334 * and closing the message box when the process is complete.
5335 * @param {String} title The title bar text
5336 * @param {String} msg The message box body text
5337 * @return {Roo.MessageBox} This message box
5339 progress : function(title, msg){
5346 minWidth: this.minProgressWidth,
5353 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5354 * If a callback function is passed it will be called after the user clicks the button, and the
5355 * id of the button that was clicked will be passed as the only parameter to the callback
5356 * (could also be the top-right close button).
5357 * @param {String} title The title bar text
5358 * @param {String} msg The message box body text
5359 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5360 * @param {Object} scope (optional) The scope of the callback function
5361 * @return {Roo.MessageBox} This message box
5363 alert : function(title, msg, fn, scope)
5378 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5379 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5380 * You are responsible for closing the message box when the process is complete.
5381 * @param {String} msg The message box body text
5382 * @param {String} title (optional) The title bar text
5383 * @return {Roo.MessageBox} This message box
5385 wait : function(msg, title){
5396 waitTimer = Roo.TaskMgr.start({
5398 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5406 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5407 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5408 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5409 * @param {String} title The title bar text
5410 * @param {String} msg The message box body text
5411 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5412 * @param {Object} scope (optional) The scope of the callback function
5413 * @return {Roo.MessageBox} This message box
5415 confirm : function(title, msg, fn, scope){
5419 buttons: this.YESNO,
5428 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5429 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5430 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5431 * (could also be the top-right close button) and the text that was entered will be passed as the two
5432 * parameters to the callback.
5433 * @param {String} title The title bar text
5434 * @param {String} msg The message box body text
5435 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5436 * @param {Object} scope (optional) The scope of the callback function
5437 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5438 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5439 * @return {Roo.MessageBox} This message box
5441 prompt : function(title, msg, fn, scope, multiline){
5445 buttons: this.OKCANCEL,
5450 multiline: multiline,
5457 * Button config that displays a single OK button
5462 * Button config that displays Yes and No buttons
5465 YESNO : {yes:true, no:true},
5467 * Button config that displays OK and Cancel buttons
5470 OKCANCEL : {ok:true, cancel:true},
5472 * Button config that displays Yes, No and Cancel buttons
5475 YESNOCANCEL : {yes:true, no:true, cancel:true},
5478 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5481 defaultTextHeight : 75,
5483 * The maximum width in pixels of the message box (defaults to 600)
5488 * The minimum width in pixels of the message box (defaults to 100)
5493 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5494 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5497 minProgressWidth : 250,
5499 * An object containing the default button text strings that can be overriden for localized language support.
5500 * Supported properties are: ok, cancel, yes and no.
5501 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5514 * Shorthand for {@link Roo.MessageBox}
5516 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5517 Roo.Msg = Roo.Msg || Roo.MessageBox;
5518 Roo.bootstrap.nav = {};
5528 * @class Roo.bootstrap.nav.Bar
5529 * @extends Roo.bootstrap.Component
5531 * Bootstrap Navbar class
5534 * Create a new Navbar
5535 * @param {Object} config The config object
5539 Roo.bootstrap.nav.Bar = function(config){
5540 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5544 * @event beforetoggle
5545 * Fire before toggle the menu
5546 * @param {Roo.EventObject} e
5548 "beforetoggle" : true
5552 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5561 getAutoCreate : function(){
5564 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5568 initEvents :function ()
5570 //Roo.log(this.el.select('.navbar-toggle',true));
5571 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5578 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5580 var size = this.el.getSize();
5581 this.maskEl.setSize(size.width, size.height);
5582 this.maskEl.enableDisplayMode("block");
5591 getChildContainer : function()
5593 if (this.el && this.el.select('.collapse').getCount()) {
5594 return this.el.select('.collapse',true).first();
5609 onToggle : function()
5612 if(this.fireEvent('beforetoggle', this) === false){
5615 var ce = this.el.select('.navbar-collapse',true).first();
5617 if (!ce.hasClass('show')) {
5627 * Expand the navbar pulldown
5629 expand : function ()
5632 var ce = this.el.select('.navbar-collapse',true).first();
5633 if (ce.hasClass('collapsing')) {
5636 ce.dom.style.height = '';
5638 ce.addClass('in'); // old...
5639 ce.removeClass('collapse');
5640 ce.addClass('show');
5641 var h = ce.getHeight();
5643 ce.removeClass('show');
5644 // at this point we should be able to see it..
5645 ce.addClass('collapsing');
5647 ce.setHeight(0); // resize it ...
5648 ce.on('transitionend', function() {
5649 //Roo.log('done transition');
5650 ce.removeClass('collapsing');
5651 ce.addClass('show');
5652 ce.removeClass('collapse');
5654 ce.dom.style.height = '';
5655 }, this, { single: true} );
5657 ce.dom.scrollTop = 0;
5660 * Collapse the navbar pulldown
5662 collapse : function()
5664 var ce = this.el.select('.navbar-collapse',true).first();
5666 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5667 // it's collapsed or collapsing..
5670 ce.removeClass('in'); // old...
5671 ce.setHeight(ce.getHeight());
5672 ce.removeClass('show');
5673 ce.addClass('collapsing');
5675 ce.on('transitionend', function() {
5676 ce.dom.style.height = '';
5677 ce.removeClass('collapsing');
5678 ce.addClass('collapse');
5679 }, this, { single: true} );
5699 * @class Roo.bootstrap.nav.Simplebar
5700 * @extends Roo.bootstrap.nav.Bar
5701 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5702 * Bootstrap Sidebar class
5704 * @cfg {Boolean} inverse is inverted color
5706 * @cfg {String} type (nav | pills | tabs)
5707 * @cfg {Boolean} arrangement stacked | justified
5708 * @cfg {String} align (left | right) alignment
5710 * @cfg {Boolean} main (true|false) main nav bar? default false
5711 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5713 * @cfg {String} tag (header|footer|nav|div) default is nav
5715 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5719 * Create a new Sidebar
5720 * @param {Object} config The config object
5724 Roo.bootstrap.nav.Simplebar = function(config){
5725 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5728 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5744 getAutoCreate : function(){
5748 tag : this.tag || 'div',
5749 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5751 if (['light','white'].indexOf(this.weight) > -1) {
5752 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5754 cfg.cls += ' bg-' + this.weight;
5757 cfg.cls += ' navbar-inverse';
5761 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5763 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5772 cls: 'nav nav-' + this.xtype,
5778 this.type = this.type || 'nav';
5779 if (['tabs','pills'].indexOf(this.type) != -1) {
5780 cfg.cn[0].cls += ' nav-' + this.type
5784 if (this.type!=='nav') {
5785 Roo.log('nav type must be nav/tabs/pills')
5787 cfg.cn[0].cls += ' navbar-nav'
5793 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5794 cfg.cn[0].cls += ' nav-' + this.arrangement;
5798 if (this.align === 'right') {
5799 cfg.cn[0].cls += ' navbar-right';
5824 * navbar-expand-md fixed-top
5828 * @class Roo.bootstrap.nav.Headerbar
5829 * @extends Roo.bootstrap.nav.Simplebar
5830 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5831 * Bootstrap Sidebar class
5833 * @cfg {String} brand what is brand
5834 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5835 * @cfg {String} brand_href href of the brand
5836 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5837 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5838 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5839 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5842 * Create a new Sidebar
5843 * @param {Object} config The config object
5847 Roo.bootstrap.nav.Headerbar = function(config){
5848 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5852 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5859 desktopCenter : false,
5862 getAutoCreate : function(){
5865 tag: this.nav || 'nav',
5866 cls: 'navbar navbar-expand-md',
5872 if (this.desktopCenter) {
5873 cn.push({cls : 'container', cn : []});
5881 cls: 'navbar-toggle navbar-toggler',
5882 'data-toggle': 'collapse',
5887 html: 'Toggle navigation'
5891 cls: 'icon-bar navbar-toggler-icon'
5904 cn.push( Roo.bootstrap.version == 4 ? btn : {
5906 cls: 'navbar-header',
5915 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5919 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5921 if (['light','white'].indexOf(this.weight) > -1) {
5922 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5924 cfg.cls += ' bg-' + this.weight;
5927 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5928 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5930 // tag can override this..
5932 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5935 if (this.brand !== '') {
5936 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5937 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5939 href: this.brand_href ? this.brand_href : '#',
5940 cls: 'navbar-brand',
5948 cfg.cls += ' main-nav';
5956 getHeaderChildContainer : function()
5958 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5959 return this.el.select('.navbar-header',true).first();
5962 return this.getChildContainer();
5965 getChildContainer : function()
5968 return this.el.select('.roo-navbar-collapse',true).first();
5973 initEvents : function()
5975 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5977 if (this.autohide) {
5982 Roo.get(document).on('scroll',function(e) {
5983 var ns = Roo.get(document).getScroll().top;
5984 var os = prevScroll;
5988 ft.removeClass('slideDown');
5989 ft.addClass('slideUp');
5992 ft.removeClass('slideUp');
5993 ft.addClass('slideDown');
6014 * @class Roo.bootstrap.nav.Sidebar
6015 * @extends Roo.bootstrap.Navbar
6016 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6017 * Bootstrap Sidebar class
6020 * Create a new Sidebar
6021 * @param {Object} config The config object
6025 Roo.bootstrap.nav.Sidebar = function(config){
6026 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6029 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6031 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6033 getAutoCreate : function(){
6038 cls: 'sidebar sidebar-nav'
6060 * @class Roo.bootstrap.nav.Group
6061 * @extends Roo.bootstrap.Component
6062 * @children Roo.bootstrap.nav.Item
6063 * Bootstrap NavGroup class
6064 * @cfg {String} align (left|right)
6065 * @cfg {Boolean} inverse
6066 * @cfg {String} type (nav|pills|tab) default nav
6067 * @cfg {String} navId - reference Id for navbar.
6068 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6071 * Create a new nav group
6072 * @param {Object} config The config object
6075 Roo.bootstrap.nav.Group = function(config){
6076 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6079 Roo.bootstrap.nav.Group.register(this);
6083 * Fires when the active item changes
6084 * @param {Roo.bootstrap.nav.Group} this
6085 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6086 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6093 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6105 getAutoCreate : function()
6107 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6113 if (Roo.bootstrap.version == 4) {
6114 if (['tabs','pills'].indexOf(this.type) != -1) {
6115 cfg.cls += ' nav-' + this.type;
6117 // trying to remove so header bar can right align top?
6118 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6119 // do not use on header bar...
6120 cfg.cls += ' navbar-nav';
6125 if (['tabs','pills'].indexOf(this.type) != -1) {
6126 cfg.cls += ' nav-' + this.type
6128 if (this.type !== 'nav') {
6129 Roo.log('nav type must be nav/tabs/pills')
6131 cfg.cls += ' navbar-nav'
6135 if (this.parent() && this.parent().sidebar) {
6138 cls: 'dashboard-menu sidebar-menu'
6144 if (this.form === true) {
6147 cls: 'navbar-form form-inline'
6149 //nav navbar-right ml-md-auto
6150 if (this.align === 'right') {
6151 cfg.cls += ' navbar-right ml-md-auto';
6153 cfg.cls += ' navbar-left';
6157 if (this.align === 'right') {
6158 cfg.cls += ' navbar-right ml-md-auto';
6160 cfg.cls += ' mr-auto';
6164 cfg.cls += ' navbar-inverse';
6172 * sets the active Navigation item
6173 * @param {Roo.bootstrap.nav.Item} the new current navitem
6175 setActiveItem : function(item)
6178 Roo.each(this.navItems, function(v){
6183 v.setActive(false, true);
6190 item.setActive(true, true);
6191 this.fireEvent('changed', this, item, prev);
6196 * gets the active Navigation item
6197 * @return {Roo.bootstrap.nav.Item} the current navitem
6199 getActive : function()
6203 Roo.each(this.navItems, function(v){
6214 indexOfNav : function()
6218 Roo.each(this.navItems, function(v,i){
6229 * adds a Navigation item
6230 * @param {Roo.bootstrap.nav.Item} the navitem to add
6232 addItem : function(cfg)
6234 if (this.form && Roo.bootstrap.version == 4) {
6237 var cn = new Roo.bootstrap.nav.Item(cfg);
6239 cn.parentId = this.id;
6240 cn.onRender(this.el, null);
6244 * register a Navigation item
6245 * @param {Roo.bootstrap.nav.Item} the navitem to add
6247 register : function(item)
6249 this.navItems.push( item);
6250 item.navId = this.navId;
6255 * clear all the Navigation item
6258 clearAll : function()
6261 this.el.dom.innerHTML = '';
6264 getNavItem: function(tabId)
6267 Roo.each(this.navItems, function(e) {
6268 if (e.tabId == tabId) {
6278 setActiveNext : function()
6280 var i = this.indexOfNav(this.getActive());
6281 if (i > this.navItems.length) {
6284 this.setActiveItem(this.navItems[i+1]);
6286 setActivePrev : function()
6288 var i = this.indexOfNav(this.getActive());
6292 this.setActiveItem(this.navItems[i-1]);
6294 clearWasActive : function(except) {
6295 Roo.each(this.navItems, function(e) {
6296 if (e.tabId != except.tabId && e.was_active) {
6297 e.was_active = false;
6304 getWasActive : function ()
6307 Roo.each(this.navItems, function(e) {
6322 Roo.apply(Roo.bootstrap.nav.Group, {
6326 * register a Navigation Group
6327 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6329 register : function(navgrp)
6331 this.groups[navgrp.navId] = navgrp;
6335 * fetch a Navigation Group based on the navigation ID
6336 * @param {string} the navgroup to add
6337 * @returns {Roo.bootstrap.nav.Group} the navgroup
6339 get: function(navId) {
6340 if (typeof(this.groups[navId]) == 'undefined') {
6342 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6344 return this.groups[navId] ;
6352 * @class Roo.bootstrap.nav.Item
6353 * @extends Roo.bootstrap.Component
6354 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6356 * Bootstrap Navbar.NavItem class
6358 * @cfg {String} href link to
6359 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6360 * @cfg {Boolean} button_outline show and outlined button
6361 * @cfg {String} html content of button
6362 * @cfg {String} badge text inside badge
6363 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6364 * @cfg {String} glyphicon DEPRICATED - use fa
6365 * @cfg {String} icon DEPRICATED - use fa
6366 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6367 * @cfg {Boolean} active Is item active
6368 * @cfg {Boolean} disabled Is item disabled
6369 * @cfg {String} linkcls Link Class
6370 * @cfg {Boolean} preventDefault (true | false) default false
6371 * @cfg {String} tabId the tab that this item activates.
6372 * @cfg {String} tagtype (a|span) render as a href or span?
6373 * @cfg {Boolean} animateRef (true|false) link to element default false
6374 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6377 * Create a new Navbar Item
6378 * @param {Object} config The config object
6380 Roo.bootstrap.nav.Item = function(config){
6381 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6386 * The raw click event for the entire grid.
6387 * @param {Roo.EventObject} e
6392 * Fires when the active item active state changes
6393 * @param {Roo.bootstrap.nav.Item} this
6394 * @param {boolean} state the new state
6400 * Fires when scroll to element
6401 * @param {Roo.bootstrap.nav.Item} this
6402 * @param {Object} options
6403 * @param {Roo.EventObject} e
6411 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6420 preventDefault : false,
6428 button_outline : false,
6432 getAutoCreate : function(){
6439 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6442 cfg.cls += ' active' ;
6444 if (this.disabled) {
6445 cfg.cls += ' disabled';
6449 if (this.button_weight.length) {
6450 cfg.tag = this.href ? 'a' : 'button';
6451 cfg.html = this.html || '';
6452 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6454 cfg.href = this.href;
6457 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6459 cfg.cls += " nav-html";
6462 // menu .. should add dropdown-menu class - so no need for carat..
6464 if (this.badge !== '') {
6466 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6471 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6475 href : this.href || "#",
6476 html: this.html || '',
6480 if (this.tagtype == 'a') {
6481 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6485 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6486 } else if (this.fa) {
6487 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6488 } else if(this.glyphicon) {
6489 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6491 cfg.cn[0].cls += " nav-html";
6495 cfg.cn[0].html += " <span class='caret'></span>";
6499 if (this.badge !== '') {
6500 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6508 onRender : function(ct, position)
6510 // Roo.log("Call onRender: " + this.xtype);
6511 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6515 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6516 this.navLink = this.el.select('.nav-link',true).first();
6517 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6522 initEvents: function()
6524 if (typeof (this.menu) != 'undefined') {
6525 this.menu.parentType = this.xtype;
6526 this.menu.triggerEl = this.el;
6527 this.menu = this.addxtype(Roo.apply({}, this.menu));
6530 this.el.on('click', this.onClick, this);
6532 //if(this.tagtype == 'span'){
6533 // this.el.select('span',true).on('click', this.onClick, this);
6536 // at this point parent should be available..
6537 this.parent().register(this);
6540 onClick : function(e)
6542 if (e.getTarget('.dropdown-menu-item')) {
6543 // did you click on a menu itemm.... - then don't trigger onclick..
6548 this.preventDefault ||
6551 Roo.log("NavItem - prevent Default?");
6555 if (this.disabled) {
6559 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6560 if (tg && tg.transition) {
6561 Roo.log("waiting for the transitionend");
6567 //Roo.log("fire event clicked");
6568 if(this.fireEvent('click', this, e) === false){
6572 if(this.tagtype == 'span'){
6576 //Roo.log(this.href);
6577 var ael = this.el.select('a',true).first();
6580 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6581 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6582 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6583 return; // ignore... - it's a 'hash' to another page.
6585 Roo.log("NavItem - prevent Default?");
6587 this.scrollToElement(e);
6591 var p = this.parent();
6593 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6594 if (typeof(p.setActiveItem) !== 'undefined') {
6595 p.setActiveItem(this);
6599 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6600 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6601 // remove the collapsed menu expand...
6602 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6606 isActive: function () {
6609 setActive : function(state, fire, is_was_active)
6611 if (this.active && !state && this.navId) {
6612 this.was_active = true;
6613 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6615 nv.clearWasActive(this);
6619 this.active = state;
6622 this.el.removeClass('active');
6623 this.navLink ? this.navLink.removeClass('active') : false;
6624 } else if (!this.el.hasClass('active')) {
6626 this.el.addClass('active');
6627 if (Roo.bootstrap.version == 4 && this.navLink ) {
6628 this.navLink.addClass('active');
6633 this.fireEvent('changed', this, state);
6636 // show a panel if it's registered and related..
6638 if (!this.navId || !this.tabId || !state || is_was_active) {
6642 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6646 var pan = tg.getPanelByName(this.tabId);
6650 // if we can not flip to new panel - go back to old nav highlight..
6651 if (false == tg.showPanel(pan)) {
6652 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6654 var onav = nv.getWasActive();
6656 onav.setActive(true, false, true);
6665 // this should not be here...
6666 setDisabled : function(state)
6668 this.disabled = state;
6670 this.el.removeClass('disabled');
6671 } else if (!this.el.hasClass('disabled')) {
6672 this.el.addClass('disabled');
6678 * Fetch the element to display the tooltip on.
6679 * @return {Roo.Element} defaults to this.el
6681 tooltipEl : function()
6683 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6686 scrollToElement : function(e)
6688 var c = document.body;
6691 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6693 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6694 c = document.documentElement;
6697 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6703 var o = target.calcOffsetsTo(c);
6710 this.fireEvent('scrollto', this, options, e);
6712 Roo.get(c).scrollTo('top', options.value, true);
6717 * Set the HTML (text content) of the item
6718 * @param {string} html content for the nav item
6720 setHtml : function(html)
6723 this.htmlEl.dom.innerHTML = html;
6735 * <span> icon </span>
6736 * <span> text </span>
6737 * <span>badge </span>
6741 * @class Roo.bootstrap.nav.SidebarItem
6742 * @extends Roo.bootstrap.nav.Item
6743 * Bootstrap Navbar.NavSidebarItem class
6745 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6746 * {Boolean} open is the menu open
6747 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6748 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6749 * {String} buttonSize (sm|md|lg)the extra classes for the button
6750 * {Boolean} showArrow show arrow next to the text (default true)
6752 * Create a new Navbar Button
6753 * @param {Object} config The config object
6755 Roo.bootstrap.nav.SidebarItem = function(config){
6756 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6761 * The raw click event for the entire grid.
6762 * @param {Roo.EventObject} e
6767 * Fires when the active item active state changes
6768 * @param {Roo.bootstrap.nav.SidebarItem} this
6769 * @param {boolean} state the new state
6777 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6779 badgeWeight : 'default',
6785 buttonWeight : 'default',
6791 getAutoCreate : function(){
6796 href : this.href || '#',
6802 if(this.buttonView){
6805 href : this.href || '#',
6806 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6819 cfg.cls += ' active';
6822 if (this.disabled) {
6823 cfg.cls += ' disabled';
6826 cfg.cls += ' open x-open';
6829 if (this.glyphicon || this.icon) {
6830 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6831 a.cn.push({ tag : 'i', cls : c }) ;
6834 if(!this.buttonView){
6837 html : this.html || ''
6844 if (this.badge !== '') {
6845 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6851 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6854 a.cls += ' dropdown-toggle treeview' ;
6860 initEvents : function()
6862 if (typeof (this.menu) != 'undefined') {
6863 this.menu.parentType = this.xtype;
6864 this.menu.triggerEl = this.el;
6865 this.menu = this.addxtype(Roo.apply({}, this.menu));
6868 this.el.on('click', this.onClick, this);
6870 if(this.badge !== ''){
6871 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6876 onClick : function(e)
6883 if(this.preventDefault){
6887 this.fireEvent('click', this, e);
6890 disable : function()
6892 this.setDisabled(true);
6897 this.setDisabled(false);
6900 setDisabled : function(state)
6902 if(this.disabled == state){
6906 this.disabled = state;
6909 this.el.addClass('disabled');
6913 this.el.removeClass('disabled');
6918 setActive : function(state)
6920 if(this.active == state){
6924 this.active = state;
6927 this.el.addClass('active');
6931 this.el.removeClass('active');
6936 isActive: function ()
6941 setBadge : function(str)
6947 this.badgeEl.dom.innerHTML = str;
6964 * @class Roo.bootstrap.nav.ProgressBar
6965 * @extends Roo.bootstrap.Component
6966 * @children Roo.bootstrap.nav.ProgressBarItem
6967 * Bootstrap NavProgressBar class
6970 * Create a new nav progress bar - a bar indicating step along a process
6971 * @param {Object} config The config object
6974 Roo.bootstrap.nav.ProgressBar = function(config){
6975 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6977 this.bullets = this.bullets || [];
6979 // Roo.bootstrap.nav.ProgressBar.register(this);
6983 * Fires when the active item changes
6984 * @param {Roo.bootstrap.nav.ProgressBar} this
6985 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6986 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
6993 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
6995 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6996 * Bullets for the Nav Progress bar for the toolbar
7001 getAutoCreate : function()
7003 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7007 cls : 'roo-navigation-bar-group',
7011 cls : 'roo-navigation-top-bar'
7015 cls : 'roo-navigation-bullets-bar',
7019 cls : 'roo-navigation-bar'
7026 cls : 'roo-navigation-bottom-bar'
7036 initEvents: function()
7041 onRender : function(ct, position)
7043 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7045 if(this.bullets.length){
7046 Roo.each(this.bullets, function(b){
7055 addItem : function(cfg)
7057 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7059 item.parentId = this.id;
7060 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7063 var top = new Roo.bootstrap.Element({
7065 cls : 'roo-navigation-bar-text'
7068 var bottom = new Roo.bootstrap.Element({
7070 cls : 'roo-navigation-bar-text'
7073 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7074 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7076 var topText = new Roo.bootstrap.Element({
7078 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7081 var bottomText = new Roo.bootstrap.Element({
7083 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7086 topText.onRender(top.el, null);
7087 bottomText.onRender(bottom.el, null);
7090 item.bottomEl = bottom;
7093 this.barItems.push(item);
7098 getActive : function()
7102 Roo.each(this.barItems, function(v){
7104 if (!v.isActive()) {
7116 setActiveItem : function(item)
7120 Roo.each(this.barItems, function(v){
7121 if (v.rid == item.rid) {
7131 item.setActive(true);
7133 this.fireEvent('changed', this, item, prev);
7136 getBarItem: function(rid)
7140 Roo.each(this.barItems, function(e) {
7152 indexOfItem : function(item)
7156 Roo.each(this.barItems, function(v, i){
7158 if (v.rid != item.rid) {
7169 setActiveNext : function()
7171 var i = this.indexOfItem(this.getActive());
7173 if (i > this.barItems.length) {
7177 this.setActiveItem(this.barItems[i+1]);
7180 setActivePrev : function()
7182 var i = this.indexOfItem(this.getActive());
7188 this.setActiveItem(this.barItems[i-1]);
7193 if(!this.barItems.length){
7197 var width = 100 / this.barItems.length;
7199 Roo.each(this.barItems, function(i){
7200 i.el.setStyle('width', width + '%');
7201 i.topEl.el.setStyle('width', width + '%');
7202 i.bottomEl.el.setStyle('width', width + '%');
7216 * @class Roo.bootstrap.nav.ProgressBarItem
7217 * @extends Roo.bootstrap.Component
7218 * Bootstrap NavProgressBarItem class
7219 * @cfg {String} rid the reference id
7220 * @cfg {Boolean} active (true|false) Is item active default false
7221 * @cfg {Boolean} disabled (true|false) Is item active default false
7222 * @cfg {String} html
7223 * @cfg {String} position (top|bottom) text position default bottom
7224 * @cfg {String} icon show icon instead of number
7227 * Create a new NavProgressBarItem
7228 * @param {Object} config The config object
7230 Roo.bootstrap.nav.ProgressBarItem = function(config){
7231 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7236 * The raw click event for the entire grid.
7237 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7238 * @param {Roo.EventObject} e
7245 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7251 position : 'bottom',
7254 getAutoCreate : function()
7256 var iconCls = 'roo-navigation-bar-item-icon';
7258 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7262 cls: 'roo-navigation-bar-item',
7272 cfg.cls += ' active';
7275 cfg.cls += ' disabled';
7281 disable : function()
7283 this.setDisabled(true);
7288 this.setDisabled(false);
7291 initEvents: function()
7293 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7295 this.iconEl.on('click', this.onClick, this);
7298 onClick : function(e)
7306 if(this.fireEvent('click', this, e) === false){
7310 this.parent().setActiveItem(this);
7313 isActive: function ()
7318 setActive : function(state)
7320 if(this.active == state){
7324 this.active = state;
7327 this.el.addClass('active');
7331 this.el.removeClass('active');
7336 setDisabled : function(state)
7338 if(this.disabled == state){
7342 this.disabled = state;
7345 this.el.addClass('disabled');
7349 this.el.removeClass('disabled');
7352 tooltipEl : function()
7354 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7360 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
7361 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
7362 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
7363 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
7365 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
7366 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
7368 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
7369 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
7371 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;/*
7377 Roo.namespace('Roo.bootstrap.breadcrumb');
7381 * @class Roo.bootstrap.breadcrumb.Nav
7382 * @extends Roo.bootstrap.Component
7383 * Bootstrap Breadcrumb Nav Class
7385 * @children Roo.bootstrap.breadcrumb.Item
7388 * Create a new breadcrumb.Nav
7389 * @param {Object} config The config object
7393 Roo.bootstrap.breadcrumb.Nav = function(config){
7394 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7399 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7401 getAutoCreate : function()
7418 initEvents: function()
7420 this.olEl = this.el.select('ol',true).first();
7422 getChildContainer : function()
7438 * @class Roo.bootstrap.breadcrumb.Nav
7439 * @extends Roo.bootstrap.Component
7440 * Bootstrap Breadcrumb Nav Class
7442 * @children Roo.bootstrap.breadcrumb.Component
7443 * @cfg {String} html the content of the link.
7444 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7445 * @cfg {Boolean} active is it active
7449 * Create a new breadcrumb.Nav
7450 * @param {Object} config The config object
7453 Roo.bootstrap.breadcrumb.Item = function(config){
7454 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7459 * The img click event for the img.
7460 * @param {Roo.EventObject} e
7467 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7472 getAutoCreate : function()
7477 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7479 if (this.href !== false) {
7486 cfg.html = this.html;
7492 initEvents: function()
7495 this.el.select('a', true).first().on('click',this.onClick, this)
7499 onClick : function(e)
7502 this.fireEvent('click',this, e);
7515 * @class Roo.bootstrap.Row
7516 * @extends Roo.bootstrap.Component
7517 * @children Roo.bootstrap.Component
7518 * Bootstrap Row class (contains columns...)
7522 * @param {Object} config The config object
7525 Roo.bootstrap.Row = function(config){
7526 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7529 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7531 getAutoCreate : function(){
7550 * @class Roo.bootstrap.Pagination
7551 * @extends Roo.bootstrap.Component
7552 * @children Roo.bootstrap.Pagination
7553 * Bootstrap Pagination class
7555 * @cfg {String} size (xs|sm|md|lg|xl)
7556 * @cfg {Boolean} inverse
7559 * Create a new Pagination
7560 * @param {Object} config The config object
7563 Roo.bootstrap.Pagination = function(config){
7564 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7567 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7573 getAutoCreate : function(){
7579 cfg.cls += ' inverse';
7585 cfg.cls += " " + this.cls;
7603 * @class Roo.bootstrap.PaginationItem
7604 * @extends Roo.bootstrap.Component
7605 * Bootstrap PaginationItem class
7606 * @cfg {String} html text
7607 * @cfg {String} href the link
7608 * @cfg {Boolean} preventDefault (true | false) default true
7609 * @cfg {Boolean} active (true | false) default false
7610 * @cfg {Boolean} disabled default false
7614 * Create a new PaginationItem
7615 * @param {Object} config The config object
7619 Roo.bootstrap.PaginationItem = function(config){
7620 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7625 * The raw click event for the entire grid.
7626 * @param {Roo.EventObject} e
7632 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7636 preventDefault: true,
7641 getAutoCreate : function(){
7647 href : this.href ? this.href : '#',
7648 html : this.html ? this.html : ''
7658 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7662 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7668 initEvents: function() {
7670 this.el.on('click', this.onClick, this);
7673 onClick : function(e)
7675 Roo.log('PaginationItem on click ');
7676 if(this.preventDefault){
7684 this.fireEvent('click', this, e);
7700 * @class Roo.bootstrap.Slider
7701 * @extends Roo.bootstrap.Component
7702 * Bootstrap Slider class
7705 * Create a new Slider
7706 * @param {Object} config The config object
7709 Roo.bootstrap.Slider = function(config){
7710 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7713 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7715 getAutoCreate : function(){
7719 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7723 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7735 * Ext JS Library 1.1.1
7736 * Copyright(c) 2006-2007, Ext JS, LLC.
7738 * Originally Released Under LGPL - original licence link has changed is not relivant.
7741 * <script type="text/javascript">
7744 * @extends Roo.dd.DDProxy
7745 * @class Roo.grid.SplitDragZone
7746 * Support for Column Header resizing
7748 * @param {Object} config
7751 // This is a support class used internally by the Grid components
7752 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7754 this.view = grid.getView();
7755 this.proxy = this.view.resizeProxy;
7756 Roo.grid.SplitDragZone.superclass.constructor.call(
7759 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7761 dragElId : Roo.id(this.proxy.dom),
7766 this.setHandleElId(Roo.id(hd));
7767 if (hd2 !== false) {
7768 this.setOuterHandleElId(Roo.id(hd2));
7771 this.scroll = false;
7773 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7774 fly: Roo.Element.fly,
7776 b4StartDrag : function(x, y){
7777 this.view.headersDisabled = true;
7778 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7779 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7781 this.proxy.setHeight(h);
7783 // for old system colWidth really stored the actual width?
7784 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7785 // which in reality did not work.. - it worked only for fixed sizes
7786 // for resizable we need to use actual sizes.
7787 var w = this.cm.getColumnWidth(this.cellIndex);
7788 if (!this.view.mainWrap) {
7790 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7795 // this was w-this.grid.minColumnWidth;
7796 // doesnt really make sense? - w = thie curren width or the rendered one?
7797 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7798 this.resetConstraints();
7799 this.setXConstraint(minw, 1000);
7800 this.setYConstraint(0, 0);
7801 this.minX = x - minw;
7802 this.maxX = x + 1000;
7804 if (!this.view.mainWrap) { // this is Bootstrap code..
7805 this.getDragEl().style.display='block';
7808 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7812 handleMouseDown : function(e){
7813 ev = Roo.EventObject.setEvent(e);
7814 var t = this.fly(ev.getTarget());
7815 if(t.hasClass("x-grid-split")){
7816 this.cellIndex = this.view.getCellIndex(t.dom);
7818 this.cm = this.grid.colModel;
7819 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7820 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7825 endDrag : function(e){
7826 this.view.headersDisabled = false;
7827 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7828 var diff = endX - this.startPos;
7830 var w = this.cm.getColumnWidth(this.cellIndex);
7831 if (!this.view.mainWrap) {
7834 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7837 autoOffset : function(){
7842 * Ext JS Library 1.1.1
7843 * Copyright(c) 2006-2007, Ext JS, LLC.
7845 * Originally Released Under LGPL - original licence link has changed is not relivant.
7848 * <script type="text/javascript">
7852 * @class Roo.grid.AbstractSelectionModel
7853 * @extends Roo.util.Observable
7855 * Abstract base class for grid SelectionModels. It provides the interface that should be
7856 * implemented by descendant classes. This class should not be directly instantiated.
7859 Roo.grid.AbstractSelectionModel = function(){
7860 this.locked = false;
7861 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7864 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7865 /** @ignore Called by the grid automatically. Do not call directly. */
7866 init : function(grid){
7872 * Locks the selections.
7879 * Unlocks the selections.
7881 unlock : function(){
7882 this.locked = false;
7886 * Returns true if the selections are locked.
7889 isLocked : function(){
7894 * Ext JS Library 1.1.1
7895 * Copyright(c) 2006-2007, Ext JS, LLC.
7897 * Originally Released Under LGPL - original licence link has changed is not relivant.
7900 * <script type="text/javascript">
7903 * @extends Roo.grid.AbstractSelectionModel
7904 * @class Roo.grid.RowSelectionModel
7905 * The default SelectionModel used by {@link Roo.grid.Grid}.
7906 * It supports multiple selections and keyboard selection/navigation.
7908 * @param {Object} config
7910 Roo.grid.RowSelectionModel = function(config){
7911 Roo.apply(this, config);
7912 this.selections = new Roo.util.MixedCollection(false, function(o){
7917 this.lastActive = false;
7921 * @event selectionchange
7922 * Fires when the selection changes
7923 * @param {SelectionModel} this
7925 "selectionchange" : true,
7927 * @event afterselectionchange
7928 * Fires after the selection changes (eg. by key press or clicking)
7929 * @param {SelectionModel} this
7931 "afterselectionchange" : true,
7933 * @event beforerowselect
7934 * Fires when a row is selected being selected, return false to cancel.
7935 * @param {SelectionModel} this
7936 * @param {Number} rowIndex The selected index
7937 * @param {Boolean} keepExisting False if other selections will be cleared
7939 "beforerowselect" : true,
7942 * Fires when a row is selected.
7943 * @param {SelectionModel} this
7944 * @param {Number} rowIndex The selected index
7945 * @param {Roo.data.Record} r The record
7949 * @event rowdeselect
7950 * Fires when a row is deselected.
7951 * @param {SelectionModel} this
7952 * @param {Number} rowIndex The selected index
7954 "rowdeselect" : true
7956 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7957 this.locked = false;
7960 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7962 * @cfg {Boolean} singleSelect
7963 * True to allow selection of only one row at a time (defaults to false)
7965 singleSelect : false,
7968 initEvents : function(){
7970 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7971 this.grid.on("mousedown", this.handleMouseDown, this);
7972 }else{ // allow click to work like normal
7973 this.grid.on("rowclick", this.handleDragableRowClick, this);
7975 // bootstrap does not have a view..
7976 var view = this.grid.view ? this.grid.view : this.grid;
7977 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7980 this.selectPrevious(e.shiftKey);
7981 }else if(this.last !== false && this.lastActive !== false){
7982 var last = this.last;
7983 this.selectRange(this.last, this.lastActive-1);
7984 view.focusRow(this.lastActive);
7989 this.selectFirstRow();
7991 this.fireEvent("afterselectionchange", this);
7993 "down" : function(e){
7995 this.selectNext(e.shiftKey);
7996 }else if(this.last !== false && this.lastActive !== false){
7997 var last = this.last;
7998 this.selectRange(this.last, this.lastActive+1);
7999 view.focusRow(this.lastActive);
8004 this.selectFirstRow();
8006 this.fireEvent("afterselectionchange", this);
8012 view.on("refresh", this.onRefresh, this);
8013 view.on("rowupdated", this.onRowUpdated, this);
8014 view.on("rowremoved", this.onRemove, this);
8018 onRefresh : function(){
8019 var ds = this.grid.ds, i, v = this.grid.view;
8020 var s = this.selections;
8022 if((i = ds.indexOfId(r.id)) != -1){
8024 s.add(ds.getAt(i)); // updating the selection relate data
8032 onRemove : function(v, index, r){
8033 this.selections.remove(r);
8037 onRowUpdated : function(v, index, r){
8038 if(this.isSelected(r)){
8039 v.onRowSelect(index);
8045 * @param {Array} records The records to select
8046 * @param {Boolean} keepExisting (optional) True to keep existing selections
8048 selectRecords : function(records, keepExisting){
8050 this.clearSelections();
8052 var ds = this.grid.ds;
8053 for(var i = 0, len = records.length; i < len; i++){
8054 this.selectRow(ds.indexOf(records[i]), true);
8059 * Gets the number of selected rows.
8062 getCount : function(){
8063 return this.selections.length;
8067 * Selects the first row in the grid.
8069 selectFirstRow : function(){
8074 * Select the last row.
8075 * @param {Boolean} keepExisting (optional) True to keep existing selections
8077 selectLastRow : function(keepExisting){
8078 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8082 * Selects the row immediately following the last selected row.
8083 * @param {Boolean} keepExisting (optional) True to keep existing selections
8085 selectNext : function(keepExisting){
8086 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8087 this.selectRow(this.last+1, keepExisting);
8088 var view = this.grid.view ? this.grid.view : this.grid;
8089 view.focusRow(this.last);
8094 * Selects the row that precedes the last selected row.
8095 * @param {Boolean} keepExisting (optional) True to keep existing selections
8097 selectPrevious : function(keepExisting){
8099 this.selectRow(this.last-1, keepExisting);
8100 var view = this.grid.view ? this.grid.view : this.grid;
8101 view.focusRow(this.last);
8106 * Returns the selected records
8107 * @return {Array} Array of selected records
8109 getSelections : function(){
8110 return [].concat(this.selections.items);
8114 * Returns the first selected record.
8117 getSelected : function(){
8118 return this.selections.itemAt(0);
8123 * Clears all selections.
8125 clearSelections : function(fast){
8130 var ds = this.grid.ds;
8131 var s = this.selections;
8133 this.deselectRow(ds.indexOfId(r.id));
8137 this.selections.clear();
8146 selectAll : function(){
8150 this.selections.clear();
8151 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8152 this.selectRow(i, true);
8157 * Returns True if there is a selection.
8160 hasSelection : function(){
8161 return this.selections.length > 0;
8165 * Returns True if the specified row is selected.
8166 * @param {Number/Record} record The record or index of the record to check
8169 isSelected : function(index){
8170 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8171 return (r && this.selections.key(r.id) ? true : false);
8175 * Returns True if the specified record id is selected.
8176 * @param {String} id The id of record to check
8179 isIdSelected : function(id){
8180 return (this.selections.key(id) ? true : false);
8184 handleMouseDown : function(e, t)
8186 var view = this.grid.view ? this.grid.view : this.grid;
8188 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8191 if(e.shiftKey && this.last !== false){
8192 var last = this.last;
8193 this.selectRange(last, rowIndex, e.ctrlKey);
8194 this.last = last; // reset the last
8195 view.focusRow(rowIndex);
8197 var isSelected = this.isSelected(rowIndex);
8198 if(e.button !== 0 && isSelected){
8199 view.focusRow(rowIndex);
8200 }else if(e.ctrlKey && isSelected){
8201 this.deselectRow(rowIndex);
8202 }else if(!isSelected){
8203 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8204 view.focusRow(rowIndex);
8207 this.fireEvent("afterselectionchange", this);
8210 handleDragableRowClick : function(grid, rowIndex, e)
8212 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8213 this.selectRow(rowIndex, false);
8214 var view = this.grid.view ? this.grid.view : this.grid;
8215 view.focusRow(rowIndex);
8216 this.fireEvent("afterselectionchange", this);
8221 * Selects multiple rows.
8222 * @param {Array} rows Array of the indexes of the row to select
8223 * @param {Boolean} keepExisting (optional) True to keep existing selections
8225 selectRows : function(rows, keepExisting){
8227 this.clearSelections();
8229 for(var i = 0, len = rows.length; i < len; i++){
8230 this.selectRow(rows[i], true);
8235 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8236 * @param {Number} startRow The index of the first row in the range
8237 * @param {Number} endRow The index of the last row in the range
8238 * @param {Boolean} keepExisting (optional) True to retain existing selections
8240 selectRange : function(startRow, endRow, keepExisting){
8245 this.clearSelections();
8247 if(startRow <= endRow){
8248 for(var i = startRow; i <= endRow; i++){
8249 this.selectRow(i, true);
8252 for(var i = startRow; i >= endRow; i--){
8253 this.selectRow(i, true);
8259 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8260 * @param {Number} startRow The index of the first row in the range
8261 * @param {Number} endRow The index of the last row in the range
8263 deselectRange : function(startRow, endRow, preventViewNotify){
8267 for(var i = startRow; i <= endRow; i++){
8268 this.deselectRow(i, preventViewNotify);
8274 * @param {Number} row The index of the row to select
8275 * @param {Boolean} keepExisting (optional) True to keep existing selections
8277 selectRow : function(index, keepExisting, preventViewNotify){
8278 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8281 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8282 if(!keepExisting || this.singleSelect){
8283 this.clearSelections();
8285 var r = this.grid.ds.getAt(index);
8286 this.selections.add(r);
8287 this.last = this.lastActive = index;
8288 if(!preventViewNotify){
8289 var view = this.grid.view ? this.grid.view : this.grid;
8290 view.onRowSelect(index);
8292 this.fireEvent("rowselect", this, index, r);
8293 this.fireEvent("selectionchange", this);
8299 * @param {Number} row The index of the row to deselect
8301 deselectRow : function(index, preventViewNotify){
8305 if(this.last == index){
8308 if(this.lastActive == index){
8309 this.lastActive = false;
8311 var r = this.grid.ds.getAt(index);
8312 this.selections.remove(r);
8313 if(!preventViewNotify){
8314 var view = this.grid.view ? this.grid.view : this.grid;
8315 view.onRowDeselect(index);
8317 this.fireEvent("rowdeselect", this, index);
8318 this.fireEvent("selectionchange", this);
8322 restoreLast : function(){
8324 this.last = this._last;
8329 acceptsNav : function(row, col, cm){
8330 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8334 onEditorKey : function(field, e){
8335 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8340 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8342 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8344 }else if(k == e.ENTER && !e.ctrlKey){
8348 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8350 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8352 }else if(k == e.ESC){
8356 g.startEditing(newCell[0], newCell[1]);
8361 * Ext JS Library 1.1.1
8362 * Copyright(c) 2006-2007, Ext JS, LLC.
8364 * Originally Released Under LGPL - original licence link has changed is not relivant.
8367 * <script type="text/javascript">
8372 * @class Roo.grid.ColumnModel
8373 * @extends Roo.util.Observable
8374 * This is the default implementation of a ColumnModel used by the Grid. It defines
8375 * the columns in the grid.
8378 var colModel = new Roo.grid.ColumnModel([
8379 {header: "Ticker", width: 60, sortable: true, locked: true},
8380 {header: "Company Name", width: 150, sortable: true},
8381 {header: "Market Cap.", width: 100, sortable: true},
8382 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8383 {header: "Employees", width: 100, sortable: true, resizable: false}
8388 * The config options listed for this class are options which may appear in each
8389 * individual column definition.
8390 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8392 * @param {Object} config An Array of column config objects. See this class's
8393 * config objects for details.
8395 Roo.grid.ColumnModel = function(config){
8397 * The config passed into the constructor
8399 this.config = []; //config;
8402 // if no id, create one
8403 // if the column does not have a dataIndex mapping,
8404 // map it to the order it is in the config
8405 for(var i = 0, len = config.length; i < len; i++){
8406 this.addColumn(config[i]);
8411 * The width of columns which have no width specified (defaults to 100)
8414 this.defaultWidth = 100;
8417 * Default sortable of columns which have no sortable specified (defaults to false)
8420 this.defaultSortable = false;
8424 * @event widthchange
8425 * Fires when the width of a column changes.
8426 * @param {ColumnModel} this
8427 * @param {Number} columnIndex The column index
8428 * @param {Number} newWidth The new width
8430 "widthchange": true,
8432 * @event headerchange
8433 * Fires when the text of a header changes.
8434 * @param {ColumnModel} this
8435 * @param {Number} columnIndex The column index
8436 * @param {Number} newText The new header text
8438 "headerchange": true,
8440 * @event hiddenchange
8441 * Fires when a column is hidden or "unhidden".
8442 * @param {ColumnModel} this
8443 * @param {Number} columnIndex The column index
8444 * @param {Boolean} hidden true if hidden, false otherwise
8446 "hiddenchange": true,
8448 * @event columnmoved
8449 * Fires when a column is moved.
8450 * @param {ColumnModel} this
8451 * @param {Number} oldIndex
8452 * @param {Number} newIndex
8454 "columnmoved" : true,
8456 * @event columlockchange
8457 * Fires when a column's locked state is changed
8458 * @param {ColumnModel} this
8459 * @param {Number} colIndex
8460 * @param {Boolean} locked true if locked
8462 "columnlockchange" : true
8464 Roo.grid.ColumnModel.superclass.constructor.call(this);
8466 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8468 * @cfg {String} header The header text to display in the Grid view.
8471 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8474 * @cfg {String} smHeader Header at Bootsrap Small width
8477 * @cfg {String} mdHeader Header at Bootsrap Medium width
8480 * @cfg {String} lgHeader Header at Bootsrap Large width
8483 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8486 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8487 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8488 * specified, the column's index is used as an index into the Record's data Array.
8491 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8492 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8495 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8496 * Defaults to the value of the {@link #defaultSortable} property.
8497 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8500 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8503 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8506 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8509 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8512 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8513 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8514 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8515 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8518 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8521 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8524 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8527 * @cfg {String} cursor (Optional)
8530 * @cfg {String} tooltip (Optional)
8533 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8536 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8539 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8542 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8545 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8548 * Returns the id of the column at the specified index.
8549 * @param {Number} index The column index
8550 * @return {String} the id
8552 getColumnId : function(index){
8553 return this.config[index].id;
8557 * Returns the column for a specified id.
8558 * @param {String} id The column id
8559 * @return {Object} the column
8561 getColumnById : function(id){
8562 return this.lookup[id];
8567 * Returns the column Object for a specified dataIndex.
8568 * @param {String} dataIndex The column dataIndex
8569 * @return {Object|Boolean} the column or false if not found
8571 getColumnByDataIndex: function(dataIndex){
8572 var index = this.findColumnIndex(dataIndex);
8573 return index > -1 ? this.config[index] : false;
8577 * Returns the index for a specified column id.
8578 * @param {String} id The column id
8579 * @return {Number} the index, or -1 if not found
8581 getIndexById : function(id){
8582 for(var i = 0, len = this.config.length; i < len; i++){
8583 if(this.config[i].id == id){
8591 * Returns the index for a specified column dataIndex.
8592 * @param {String} dataIndex The column dataIndex
8593 * @return {Number} the index, or -1 if not found
8596 findColumnIndex : function(dataIndex){
8597 for(var i = 0, len = this.config.length; i < len; i++){
8598 if(this.config[i].dataIndex == dataIndex){
8606 moveColumn : function(oldIndex, newIndex){
8607 var c = this.config[oldIndex];
8608 this.config.splice(oldIndex, 1);
8609 this.config.splice(newIndex, 0, c);
8610 this.dataMap = null;
8611 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8614 isLocked : function(colIndex){
8615 return this.config[colIndex].locked === true;
8618 setLocked : function(colIndex, value, suppressEvent){
8619 if(this.isLocked(colIndex) == value){
8622 this.config[colIndex].locked = value;
8624 this.fireEvent("columnlockchange", this, colIndex, value);
8628 getTotalLockedWidth : function(){
8630 for(var i = 0; i < this.config.length; i++){
8631 if(this.isLocked(i) && !this.isHidden(i)){
8632 this.totalWidth += this.getColumnWidth(i);
8638 getLockedCount : function(){
8639 for(var i = 0, len = this.config.length; i < len; i++){
8640 if(!this.isLocked(i)){
8645 return this.config.length;
8649 * Returns the number of columns.
8652 getColumnCount : function(visibleOnly){
8653 if(visibleOnly === true){
8655 for(var i = 0, len = this.config.length; i < len; i++){
8656 if(!this.isHidden(i)){
8662 return this.config.length;
8666 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8667 * @param {Function} fn
8668 * @param {Object} scope (optional)
8669 * @return {Array} result
8671 getColumnsBy : function(fn, scope){
8673 for(var i = 0, len = this.config.length; i < len; i++){
8674 var c = this.config[i];
8675 if(fn.call(scope||this, c, i) === true){
8683 * Returns true if the specified column is sortable.
8684 * @param {Number} col The column index
8687 isSortable : function(col){
8688 if(typeof this.config[col].sortable == "undefined"){
8689 return this.defaultSortable;
8691 return this.config[col].sortable;
8695 * Returns the rendering (formatting) function defined for the column.
8696 * @param {Number} col The column index.
8697 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8699 getRenderer : function(col){
8700 if(!this.config[col].renderer){
8701 return Roo.grid.ColumnModel.defaultRenderer;
8703 return this.config[col].renderer;
8707 * Sets the rendering (formatting) function for a column.
8708 * @param {Number} col The column index
8709 * @param {Function} fn The function to use to process the cell's raw data
8710 * to return HTML markup for the grid view. The render function is called with
8711 * the following parameters:<ul>
8712 * <li>Data value.</li>
8713 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8714 * <li>css A CSS style string to apply to the table cell.</li>
8715 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8716 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8717 * <li>Row index</li>
8718 * <li>Column index</li>
8719 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8721 setRenderer : function(col, fn){
8722 this.config[col].renderer = fn;
8726 * Returns the width for the specified column.
8727 * @param {Number} col The column index
8728 * @param (optional) {String} gridSize bootstrap width size.
8731 getColumnWidth : function(col, gridSize)
8733 var cfg = this.config[col];
8735 if (typeof(gridSize) == 'undefined') {
8736 return cfg.width * 1 || this.defaultWidth;
8738 if (gridSize === false) { // if we set it..
8739 return cfg.width || false;
8741 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8743 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8744 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8747 return cfg[ sizes[i] ];
8754 * Sets the width for a column.
8755 * @param {Number} col The column index
8756 * @param {Number} width The new width
8758 setColumnWidth : function(col, width, suppressEvent){
8759 this.config[col].width = width;
8760 this.totalWidth = null;
8762 this.fireEvent("widthchange", this, col, width);
8767 * Returns the total width of all columns.
8768 * @param {Boolean} includeHidden True to include hidden column widths
8771 getTotalWidth : function(includeHidden){
8772 if(!this.totalWidth){
8773 this.totalWidth = 0;
8774 for(var i = 0, len = this.config.length; i < len; i++){
8775 if(includeHidden || !this.isHidden(i)){
8776 this.totalWidth += this.getColumnWidth(i);
8780 return this.totalWidth;
8784 * Returns the header for the specified column.
8785 * @param {Number} col The column index
8788 getColumnHeader : function(col){
8789 return this.config[col].header;
8793 * Sets the header for a column.
8794 * @param {Number} col The column index
8795 * @param {String} header The new header
8797 setColumnHeader : function(col, header){
8798 this.config[col].header = header;
8799 this.fireEvent("headerchange", this, col, header);
8803 * Returns the tooltip for the specified column.
8804 * @param {Number} col The column index
8807 getColumnTooltip : function(col){
8808 return this.config[col].tooltip;
8811 * Sets the tooltip for a column.
8812 * @param {Number} col The column index
8813 * @param {String} tooltip The new tooltip
8815 setColumnTooltip : function(col, tooltip){
8816 this.config[col].tooltip = tooltip;
8820 * Returns the dataIndex for the specified column.
8821 * @param {Number} col The column index
8824 getDataIndex : function(col){
8825 return this.config[col].dataIndex;
8829 * Sets the dataIndex for a column.
8830 * @param {Number} col The column index
8831 * @param {Number} dataIndex The new dataIndex
8833 setDataIndex : function(col, dataIndex){
8834 this.config[col].dataIndex = dataIndex;
8840 * Returns true if the cell is editable.
8841 * @param {Number} colIndex The column index
8842 * @param {Number} rowIndex The row index - this is nto actually used..?
8845 isCellEditable : function(colIndex, rowIndex){
8846 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8850 * Returns the editor defined for the cell/column.
8851 * return false or null to disable editing.
8852 * @param {Number} colIndex The column index
8853 * @param {Number} rowIndex The row index
8856 getCellEditor : function(colIndex, rowIndex){
8857 return this.config[colIndex].editor;
8861 * Sets if a column is editable.
8862 * @param {Number} col The column index
8863 * @param {Boolean} editable True if the column is editable
8865 setEditable : function(col, editable){
8866 this.config[col].editable = editable;
8871 * Returns true if the column is hidden.
8872 * @param {Number} colIndex The column index
8875 isHidden : function(colIndex){
8876 return this.config[colIndex].hidden;
8881 * Returns true if the column width cannot be changed
8883 isFixed : function(colIndex){
8884 return this.config[colIndex].fixed;
8888 * Returns true if the column can be resized
8891 isResizable : function(colIndex){
8892 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8895 * Sets if a column is hidden.
8896 * @param {Number} colIndex The column index
8897 * @param {Boolean} hidden True if the column is hidden
8899 setHidden : function(colIndex, hidden){
8900 this.config[colIndex].hidden = hidden;
8901 this.totalWidth = null;
8902 this.fireEvent("hiddenchange", this, colIndex, hidden);
8906 * Sets the editor for a column.
8907 * @param {Number} col The column index
8908 * @param {Object} editor The editor object
8910 setEditor : function(col, editor){
8911 this.config[col].editor = editor;
8914 * Add a column (experimental...) - defaults to adding to the end..
8915 * @param {Object} config
8917 addColumn : function(c)
8920 var i = this.config.length;
8923 if(typeof c.dataIndex == "undefined"){
8926 if(typeof c.renderer == "string"){
8927 c.renderer = Roo.util.Format[c.renderer];
8929 if(typeof c.id == "undefined"){
8932 if(c.editor && c.editor.xtype){
8933 c.editor = Roo.factory(c.editor, Roo.grid);
8935 if(c.editor && c.editor.isFormField){
8936 c.editor = new Roo.grid.GridEditor(c.editor);
8938 this.lookup[c.id] = c;
8943 Roo.grid.ColumnModel.defaultRenderer = function(value)
8945 if(typeof value == "object") {
8948 if(typeof value == "string" && value.length < 1){
8952 return String.format("{0}", value);
8955 // Alias for backwards compatibility
8956 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8959 * Ext JS Library 1.1.1
8960 * Copyright(c) 2006-2007, Ext JS, LLC.
8962 * Originally Released Under LGPL - original licence link has changed is not relivant.
8965 * <script type="text/javascript">
8969 * @class Roo.LoadMask
8970 * A simple utility class for generically masking elements while loading data. If the element being masked has
8971 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8972 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8973 * element's UpdateManager load indicator and will be destroyed after the initial load.
8975 * Create a new LoadMask
8976 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8977 * @param {Object} config The config object
8979 Roo.LoadMask = function(el, config){
8980 this.el = Roo.get(el);
8981 Roo.apply(this, config);
8983 this.store.on('beforeload', this.onBeforeLoad, this);
8984 this.store.on('load', this.onLoad, this);
8985 this.store.on('loadexception', this.onLoadException, this);
8986 this.removeMask = false;
8988 var um = this.el.getUpdateManager();
8989 um.showLoadIndicator = false; // disable the default indicator
8990 um.on('beforeupdate', this.onBeforeLoad, this);
8991 um.on('update', this.onLoad, this);
8992 um.on('failure', this.onLoad, this);
8993 this.removeMask = true;
8997 Roo.LoadMask.prototype = {
8999 * @cfg {Boolean} removeMask
9000 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9001 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9006 * The text to display in a centered loading message box (defaults to 'Loading...')
9010 * @cfg {String} msgCls
9011 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9013 msgCls : 'x-mask-loading',
9016 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9022 * Disables the mask to prevent it from being displayed
9024 disable : function(){
9025 this.disabled = true;
9029 * Enables the mask so that it can be displayed
9031 enable : function(){
9032 this.disabled = false;
9035 onLoadException : function()
9039 if (typeof(arguments[3]) != 'undefined') {
9040 Roo.MessageBox.alert("Error loading",arguments[3]);
9044 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9045 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9052 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9057 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9061 onBeforeLoad : function(){
9063 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9068 destroy : function(){
9070 this.store.un('beforeload', this.onBeforeLoad, this);
9071 this.store.un('load', this.onLoad, this);
9072 this.store.un('loadexception', this.onLoadException, this);
9074 var um = this.el.getUpdateManager();
9075 um.un('beforeupdate', this.onBeforeLoad, this);
9076 um.un('update', this.onLoad, this);
9077 um.un('failure', this.onLoad, this);
9081 * @class Roo.bootstrap.Table
9083 * @extends Roo.bootstrap.Component
9084 * @children Roo.bootstrap.TableBody
9085 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9086 * Similar to Roo.grid.Grid
9088 var table = Roo.factory({
9090 xns : Roo.bootstrap,
9091 autoSizeColumns: true,
9098 sortInfo : { direction : 'ASC', field: 'name' },
9100 xtype : 'HttpProxy',
9103 url : 'https://example.com/some.data.url.json'
9106 xtype : 'JsonReader',
9108 fields : [ 'id', 'name', whatever' ],
9115 xtype : 'ColumnModel',
9119 dataIndex : 'is_in_group',
9122 renderer : function(v, x , r) {
9124 return String.format("{0}", v)
9130 xtype : 'RowSelectionModel',
9131 xns : Roo.bootstrap.Table
9132 // you can add listeners to catch selection change here....
9138 grid.render(Roo.get("some-div"));
9141 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9146 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9147 * @cfg {Roo.data.Store} store The data store to use
9148 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9150 * @cfg {String} cls table class
9153 * @cfg {boolean} striped Should the rows be alternative striped
9154 * @cfg {boolean} bordered Add borders to the table
9155 * @cfg {boolean} hover Add hover highlighting
9156 * @cfg {boolean} condensed Format condensed
9157 * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9158 * also adds table-responsive (see bootstrap docs for details)
9159 * @cfg {Boolean} loadMask (true|false) default false
9160 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9161 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9162 * @cfg {Boolean} rowSelection (true|false) default false
9163 * @cfg {Boolean} cellSelection (true|false) default false
9164 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
9165 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9166 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9167 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9168 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
9169 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9172 * Create a new Table
9173 * @param {Object} config The config object
9176 Roo.bootstrap.Table = function(config)
9178 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9181 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9182 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9183 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9184 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9186 this.view = this; // compat with grid.
9188 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9190 this.sm.grid = this;
9191 this.selModel = Roo.factory(this.sm, Roo.grid);
9192 this.sm = this.selModel;
9193 this.sm.xmodule = this.xmodule || false;
9196 if (this.cm && typeof(this.cm.config) == 'undefined') {
9197 this.colModel = new Roo.grid.ColumnModel(this.cm);
9198 this.cm = this.colModel;
9199 this.cm.xmodule = this.xmodule || false;
9202 this.store= Roo.factory(this.store, Roo.data);
9203 this.ds = this.store;
9204 this.ds.xmodule = this.xmodule || false;
9207 if (this.footer && this.store) {
9208 this.footer.dataSource = this.ds;
9209 this.footer = Roo.factory(this.footer);
9216 * Fires when a cell is clicked
9217 * @param {Roo.bootstrap.Table} this
9218 * @param {Roo.Element} el
9219 * @param {Number} rowIndex
9220 * @param {Number} columnIndex
9221 * @param {Roo.EventObject} e
9225 * @event celldblclick
9226 * Fires when a cell is double clicked
9227 * @param {Roo.bootstrap.Table} this
9228 * @param {Roo.Element} el
9229 * @param {Number} rowIndex
9230 * @param {Number} columnIndex
9231 * @param {Roo.EventObject} e
9233 "celldblclick" : true,
9236 * Fires when a row is clicked
9237 * @param {Roo.bootstrap.Table} this
9238 * @param {Roo.Element} el
9239 * @param {Number} rowIndex
9240 * @param {Roo.EventObject} e
9244 * @event rowdblclick
9245 * Fires when a row is double clicked
9246 * @param {Roo.bootstrap.Table} this
9247 * @param {Roo.Element} el
9248 * @param {Number} rowIndex
9249 * @param {Roo.EventObject} e
9251 "rowdblclick" : true,
9254 * Fires when a mouseover occur
9255 * @param {Roo.bootstrap.Table} this
9256 * @param {Roo.Element} el
9257 * @param {Number} rowIndex
9258 * @param {Number} columnIndex
9259 * @param {Roo.EventObject} e
9264 * Fires when a mouseout occur
9265 * @param {Roo.bootstrap.Table} this
9266 * @param {Roo.Element} el
9267 * @param {Number} rowIndex
9268 * @param {Number} columnIndex
9269 * @param {Roo.EventObject} e
9274 * Fires when a row is rendered, so you can change add a style to it.
9275 * @param {Roo.bootstrap.Table} this
9276 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9280 * @event rowsrendered
9281 * Fires when all the rows have been rendered
9282 * @param {Roo.bootstrap.Table} this
9284 'rowsrendered' : true,
9286 * @event contextmenu
9287 * The raw contextmenu event for the entire grid.
9288 * @param {Roo.EventObject} e
9290 "contextmenu" : true,
9292 * @event rowcontextmenu
9293 * Fires when a row is right clicked
9294 * @param {Roo.bootstrap.Table} this
9295 * @param {Number} rowIndex
9296 * @param {Roo.EventObject} e
9298 "rowcontextmenu" : true,
9300 * @event cellcontextmenu
9301 * Fires when a cell is right clicked
9302 * @param {Roo.bootstrap.Table} this
9303 * @param {Number} rowIndex
9304 * @param {Number} cellIndex
9305 * @param {Roo.EventObject} e
9307 "cellcontextmenu" : true,
9309 * @event headercontextmenu
9310 * Fires when a header is right clicked
9311 * @param {Roo.bootstrap.Table} this
9312 * @param {Number} columnIndex
9313 * @param {Roo.EventObject} e
9315 "headercontextmenu" : true,
9318 * The raw mousedown event for the entire grid.
9319 * @param {Roo.EventObject} e
9326 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9342 enableColumnResize: true,
9344 rowSelection : false,
9345 cellSelection : false,
9348 minColumnWidth : 50,
9350 // Roo.Element - the tbody
9351 bodyEl: false, // <tbody> Roo.Element - thead element
9352 headEl: false, // <thead> Roo.Element - thead element
9353 resizeProxy : false, // proxy element for dragging?
9357 container: false, // used by gridpanel...
9363 auto_hide_footer : false,
9365 view: false, // actually points to this..
9367 getAutoCreate : function()
9369 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9376 // this get's auto added by panel.Grid
9377 if (this.scrollBody) {
9378 cfg.cls += ' table-body-fixed';
9381 cfg.cls += ' table-striped';
9385 cfg.cls += ' table-hover';
9387 if (this.bordered) {
9388 cfg.cls += ' table-bordered';
9390 if (this.condensed) {
9391 cfg.cls += ' table-condensed';
9394 if (this.responsive) {
9395 cfg.cls += ' table-responsive';
9399 cfg.cls+= ' ' +this.cls;
9405 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9408 if(this.store || this.cm){
9409 if(this.headerShow){
9410 cfg.cn.push(this.renderHeader());
9413 cfg.cn.push(this.renderBody());
9415 if(this.footerShow){
9416 cfg.cn.push(this.renderFooter());
9418 // where does this come from?
9419 //cfg.cls+= ' TableGrid';
9422 return { cn : [ cfg ] };
9425 initEvents : function()
9427 if(!this.store || !this.cm){
9430 if (this.selModel) {
9431 this.selModel.initEvents();
9435 //Roo.log('initEvents with ds!!!!');
9437 this.bodyEl = this.el.select('tbody', true).first();
9438 this.headEl = this.el.select('thead', true).first();
9439 this.mainFoot = this.el.select('tfoot', true).first();
9444 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9445 e.on('click', this.sort, this);
9449 // why is this done????? = it breaks dialogs??
9450 //this.parent().el.setStyle('position', 'relative');
9454 this.footer.parentId = this.id;
9455 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9458 this.el.select('tfoot tr td').first().addClass('hide');
9463 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9466 this.store.on('load', this.onLoad, this);
9467 this.store.on('beforeload', this.onBeforeLoad, this);
9468 this.store.on('update', this.onUpdate, this);
9469 this.store.on('add', this.onAdd, this);
9470 this.store.on("clear", this.clear, this);
9472 this.el.on("contextmenu", this.onContextMenu, this);
9475 this.cm.on("headerchange", this.onHeaderChange, this);
9476 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9478 //?? does bodyEl get replaced on render?
9479 this.bodyEl.on("click", this.onClick, this);
9480 this.bodyEl.on("dblclick", this.onDblClick, this);
9481 this.bodyEl.on('scroll', this.onBodyScroll, this);
9483 // guessing mainbody will work - this relays usually caught by selmodel at present.
9484 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9487 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9490 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9491 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9496 // Compatibility with grid - we implement all the view features at present.
9497 getView : function()
9502 initCSS : function()
9506 var cm = this.cm, styles = [];
9507 this.CSS.removeStyleSheet(this.id + '-cssrules');
9508 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9509 // we can honour xs/sm/md/xl as widths...
9510 // we first have to decide what widht we are currently at...
9511 var sz = Roo.getGridSize();
9515 var cols = []; // visable cols.
9517 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9518 var w = cm.getColumnWidth(i, false);
9520 cols.push( { rel : false, abs : 0 });
9524 cols.push( { rel : false, abs : w });
9526 last = i; // not really..
9529 var w = cm.getColumnWidth(i, sz);
9534 cols.push( { rel : w, abs : false });
9537 var avail = this.bodyEl.dom.clientWidth - total_abs;
9539 var unitWidth = Math.floor(avail / total);
9540 var rem = avail - (unitWidth * total);
9542 var hidden, width, pos = 0 , splithide , left;
9543 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9545 hidden = 'display:none;';
9547 width = 'width:0px;';
9549 if(!cm.isHidden(i)){
9553 // we can honour xs/sm/md/xl ?
9554 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9556 hidden = 'display:none;';
9558 // width should return a small number...
9560 w+=rem; // add the remaining with..
9563 left = "left:" + (pos -4) + "px;";
9564 width = "width:" + w+ "px;";
9567 if (this.responsive) {
9570 hidden = cm.isHidden(i) ? 'display:none;' : '';
9571 splithide = 'display: none;';
9574 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9577 splithide = 'display:none;';
9580 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9581 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9586 //Roo.log(styles.join(''));
9587 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9593 onContextMenu : function(e, t)
9595 this.processEvent("contextmenu", e);
9598 processEvent : function(name, e)
9600 if (name != 'touchstart' ) {
9601 this.fireEvent(name, e);
9604 var t = e.getTarget();
9606 var cell = Roo.get(t);
9612 if(cell.findParent('tfoot', false, true)){
9616 if(cell.findParent('thead', false, true)){
9618 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9619 cell = Roo.get(t).findParent('th', false, true);
9621 Roo.log("failed to find th in thead?");
9622 Roo.log(e.getTarget());
9627 var cellIndex = cell.dom.cellIndex;
9629 var ename = name == 'touchstart' ? 'click' : name;
9630 this.fireEvent("header" + ename, this, cellIndex, e);
9635 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9636 cell = Roo.get(t).findParent('td', false, true);
9638 Roo.log("failed to find th in tbody?");
9639 Roo.log(e.getTarget());
9644 var row = cell.findParent('tr', false, true);
9645 var cellIndex = cell.dom.cellIndex;
9646 var rowIndex = row.dom.rowIndex - 1;
9650 this.fireEvent("row" + name, this, rowIndex, e);
9654 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9660 onMouseover : function(e, el)
9662 var cell = Roo.get(el);
9668 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9669 cell = cell.findParent('td', false, true);
9672 var row = cell.findParent('tr', false, true);
9673 var cellIndex = cell.dom.cellIndex;
9674 var rowIndex = row.dom.rowIndex - 1; // start from 0
9676 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9680 onMouseout : function(e, el)
9682 var cell = Roo.get(el);
9688 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9689 cell = cell.findParent('td', false, true);
9692 var row = cell.findParent('tr', false, true);
9693 var cellIndex = cell.dom.cellIndex;
9694 var rowIndex = row.dom.rowIndex - 1; // start from 0
9696 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9700 onClick : function(e, el)
9702 var cell = Roo.get(el);
9704 if(!cell || (!this.cellSelection && !this.rowSelection)){
9708 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9709 cell = cell.findParent('td', false, true);
9712 if(!cell || typeof(cell) == 'undefined'){
9716 var row = cell.findParent('tr', false, true);
9718 if(!row || typeof(row) == 'undefined'){
9722 var cellIndex = cell.dom.cellIndex;
9723 var rowIndex = this.getRowIndex(row);
9725 // why??? - should these not be based on SelectionModel?
9726 //if(this.cellSelection){
9727 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9730 //if(this.rowSelection){
9731 this.fireEvent('rowclick', this, row, rowIndex, e);
9736 onDblClick : function(e,el)
9738 var cell = Roo.get(el);
9740 if(!cell || (!this.cellSelection && !this.rowSelection)){
9744 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9745 cell = cell.findParent('td', false, true);
9748 if(!cell || typeof(cell) == 'undefined'){
9752 var row = cell.findParent('tr', false, true);
9754 if(!row || typeof(row) == 'undefined'){
9758 var cellIndex = cell.dom.cellIndex;
9759 var rowIndex = this.getRowIndex(row);
9761 if(this.cellSelection){
9762 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9765 if(this.rowSelection){
9766 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9769 findRowIndex : function(el)
9771 var cell = Roo.get(el);
9775 var row = cell.findParent('tr', false, true);
9777 if(!row || typeof(row) == 'undefined'){
9780 return this.getRowIndex(row);
9782 sort : function(e,el)
9784 var col = Roo.get(el);
9786 if(!col.hasClass('sortable')){
9790 var sort = col.attr('sort');
9793 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9797 this.store.sortInfo = {field : sort, direction : dir};
9800 Roo.log("calling footer first");
9801 this.footer.onClick('first');
9804 this.store.load({ params : { start : 0 } });
9808 renderHeader : function()
9816 this.totalWidth = 0;
9818 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9820 var config = cm.config[i];
9824 cls : 'x-hcol-' + i,
9827 html: cm.getColumnHeader(i)
9830 var tooltip = cm.getColumnTooltip(i);
9832 c.tooltip = tooltip;
9838 if(typeof(config.sortable) != 'undefined' && config.sortable){
9839 c.cls += ' sortable';
9840 c.html = '<i class="fa"></i>' + c.html;
9843 // could use BS4 hidden-..-down
9845 if(typeof(config.lgHeader) != 'undefined'){
9846 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9849 if(typeof(config.mdHeader) != 'undefined'){
9850 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9853 if(typeof(config.smHeader) != 'undefined'){
9854 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9857 if(typeof(config.xsHeader) != 'undefined'){
9858 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9865 if(typeof(config.tooltip) != 'undefined'){
9866 c.tooltip = config.tooltip;
9869 if(typeof(config.colspan) != 'undefined'){
9870 c.colspan = config.colspan;
9873 // hidden is handled by CSS now
9875 if(typeof(config.dataIndex) != 'undefined'){
9876 c.sort = config.dataIndex;
9881 if(typeof(config.align) != 'undefined' && config.align.length){
9882 c.style += ' text-align:' + config.align + ';';
9885 /* width is done in CSS
9886 *if(typeof(config.width) != 'undefined'){
9887 c.style += ' width:' + config.width + 'px;';
9888 this.totalWidth += config.width;
9890 this.totalWidth += 100; // assume minimum of 100 per column?
9894 if(typeof(config.cls) != 'undefined'){
9895 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9897 // this is the bit that doesnt reall work at all...
9899 if (this.responsive) {
9902 ['xs','sm','md','lg'].map(function(size){
9904 if(typeof(config[size]) == 'undefined'){
9908 if (!config[size]) { // 0 = hidden
9909 // BS 4 '0' is treated as hide that column and below.
9910 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9914 c.cls += ' col-' + size + '-' + config[size] + (
9915 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9923 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9934 renderBody : function()
9944 colspan : this.cm.getColumnCount()
9954 renderFooter : function()
9964 colspan : this.cm.getColumnCount()
9978 // Roo.log('ds onload');
9983 var ds = this.store;
9985 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9986 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9987 if (_this.store.sortInfo) {
9989 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9990 e.select('i', true).addClass(['fa-arrow-up']);
9993 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9994 e.select('i', true).addClass(['fa-arrow-down']);
9999 var tbody = this.bodyEl;
10001 if(ds.getCount() > 0){
10002 ds.data.each(function(d,rowIndex){
10003 var row = this.renderRow(cm, ds, rowIndex);
10005 tbody.createChild(row);
10009 if(row.cellObjects.length){
10010 Roo.each(row.cellObjects, function(r){
10011 _this.renderCellObject(r);
10018 var tfoot = this.el.select('tfoot', true).first();
10020 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10022 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10024 var total = this.ds.getTotalCount();
10026 if(this.footer.pageSize < total){
10027 this.mainFoot.show();
10031 Roo.each(this.el.select('tbody td', true).elements, function(e){
10032 e.on('mouseover', _this.onMouseover, _this);
10035 Roo.each(this.el.select('tbody td', true).elements, function(e){
10036 e.on('mouseout', _this.onMouseout, _this);
10038 this.fireEvent('rowsrendered', this);
10042 this.initCSS(); /// resize cols
10048 onUpdate : function(ds,record)
10050 this.refreshRow(record);
10054 onRemove : function(ds, record, index, isUpdate){
10055 if(isUpdate !== true){
10056 this.fireEvent("beforerowremoved", this, index, record);
10058 var bt = this.bodyEl.dom;
10060 var rows = this.el.select('tbody > tr', true).elements;
10062 if(typeof(rows[index]) != 'undefined'){
10063 bt.removeChild(rows[index].dom);
10066 // if(bt.rows[index]){
10067 // bt.removeChild(bt.rows[index]);
10070 if(isUpdate !== true){
10071 //this.stripeRows(index);
10072 //this.syncRowHeights(index, index);
10074 this.fireEvent("rowremoved", this, index, record);
10078 onAdd : function(ds, records, rowIndex)
10080 //Roo.log('on Add called');
10081 // - note this does not handle multiple adding very well..
10082 var bt = this.bodyEl.dom;
10083 for (var i =0 ; i < records.length;i++) {
10084 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10085 //Roo.log(records[i]);
10086 //Roo.log(this.store.getAt(rowIndex+i));
10087 this.insertRow(this.store, rowIndex + i, false);
10094 refreshRow : function(record){
10095 var ds = this.store, index;
10096 if(typeof record == 'number'){
10098 record = ds.getAt(index);
10100 index = ds.indexOf(record);
10102 return; // should not happen - but seems to
10105 this.insertRow(ds, index, true);
10107 this.onRemove(ds, record, index+1, true);
10109 //this.syncRowHeights(index, index);
10111 this.fireEvent("rowupdated", this, index, record);
10113 // private - called by RowSelection
10114 onRowSelect : function(rowIndex){
10115 var row = this.getRowDom(rowIndex);
10116 row.addClass(['bg-info','info']);
10118 // private - called by RowSelection
10119 onRowDeselect : function(rowIndex)
10121 if (rowIndex < 0) {
10124 var row = this.getRowDom(rowIndex);
10125 row.removeClass(['bg-info','info']);
10128 * Focuses the specified row.
10129 * @param {Number} row The row index
10131 focusRow : function(row)
10133 //Roo.log('GridView.focusRow');
10134 var x = this.bodyEl.dom.scrollLeft;
10135 this.focusCell(row, 0, false);
10136 this.bodyEl.dom.scrollLeft = x;
10140 * Focuses the specified cell.
10141 * @param {Number} row The row index
10142 * @param {Number} col The column index
10143 * @param {Boolean} hscroll false to disable horizontal scrolling
10145 focusCell : function(row, col, hscroll)
10147 //Roo.log('GridView.focusCell');
10148 var el = this.ensureVisible(row, col, hscroll);
10149 // not sure what focusEL achives = it's a <a> pos relative
10150 //this.focusEl.alignTo(el, "tl-tl");
10152 // this.focusEl.focus();
10154 // this.focusEl.focus.defer(1, this.focusEl);
10159 * Scrolls the specified cell into view
10160 * @param {Number} row The row index
10161 * @param {Number} col The column index
10162 * @param {Boolean} hscroll false to disable horizontal scrolling
10164 ensureVisible : function(row, col, hscroll)
10166 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10167 //return null; //disable for testing.
10168 if(typeof row != "number"){
10169 row = row.rowIndex;
10171 if(row < 0 && row >= this.ds.getCount()){
10174 col = (col !== undefined ? col : 0);
10176 while(cm.isHidden(col)){
10180 var el = this.getCellDom(row, col);
10184 var c = this.bodyEl.dom;
10186 var ctop = parseInt(el.offsetTop, 10);
10187 var cleft = parseInt(el.offsetLeft, 10);
10188 var cbot = ctop + el.offsetHeight;
10189 var cright = cleft + el.offsetWidth;
10191 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10192 var ch = 0; //?? header is not withing the area?
10193 var stop = parseInt(c.scrollTop, 10);
10194 var sleft = parseInt(c.scrollLeft, 10);
10195 var sbot = stop + ch;
10196 var sright = sleft + c.clientWidth;
10198 Roo.log('GridView.ensureVisible:' +
10200 ' c.clientHeight:' + c.clientHeight +
10201 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10209 c.scrollTop = ctop;
10210 //Roo.log("set scrolltop to ctop DISABLE?");
10211 }else if(cbot > sbot){
10212 //Roo.log("set scrolltop to cbot-ch");
10213 c.scrollTop = cbot-ch;
10216 if(hscroll !== false){
10218 c.scrollLeft = cleft;
10219 }else if(cright > sright){
10220 c.scrollLeft = cright-c.clientWidth;
10228 insertRow : function(dm, rowIndex, isUpdate){
10231 this.fireEvent("beforerowsinserted", this, rowIndex);
10233 //var s = this.getScrollState();
10234 var row = this.renderRow(this.cm, this.store, rowIndex);
10235 // insert before rowIndex..
10236 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10240 if(row.cellObjects.length){
10241 Roo.each(row.cellObjects, function(r){
10242 _this.renderCellObject(r);
10247 this.fireEvent("rowsinserted", this, rowIndex);
10248 //this.syncRowHeights(firstRow, lastRow);
10249 //this.stripeRows(firstRow);
10256 getRowDom : function(rowIndex)
10258 var rows = this.el.select('tbody > tr', true).elements;
10260 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10263 getCellDom : function(rowIndex, colIndex)
10265 var row = this.getRowDom(rowIndex);
10266 if (row === false) {
10269 var cols = row.select('td', true).elements;
10270 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10274 // returns the object tree for a tr..
10277 renderRow : function(cm, ds, rowIndex)
10279 var d = ds.getAt(rowIndex);
10283 cls : 'x-row-' + rowIndex,
10287 var cellObjects = [];
10289 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10290 var config = cm.config[i];
10292 var renderer = cm.getRenderer(i);
10296 if(typeof(renderer) !== 'undefined'){
10297 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10299 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10300 // and are rendered into the cells after the row is rendered - using the id for the element.
10302 if(typeof(value) === 'object'){
10312 rowIndex : rowIndex,
10317 this.fireEvent('rowclass', this, rowcfg);
10321 // this might end up displaying HTML?
10322 // this is too messy... - better to only do it on columsn you know are going to be too long
10323 //tooltip : (typeof(value) === 'object') ? '' : value,
10324 cls : rowcfg.rowClass + ' x-col-' + i,
10326 html: (typeof(value) === 'object') ? '' : value
10333 if(typeof(config.colspan) != 'undefined'){
10334 td.colspan = config.colspan;
10339 if(typeof(config.align) != 'undefined' && config.align.length){
10340 td.style += ' text-align:' + config.align + ';';
10342 if(typeof(config.valign) != 'undefined' && config.valign.length){
10343 td.style += ' vertical-align:' + config.valign + ';';
10346 if(typeof(config.width) != 'undefined'){
10347 td.style += ' width:' + config.width + 'px;';
10351 if(typeof(config.cursor) != 'undefined'){
10352 td.style += ' cursor:' + config.cursor + ';';
10355 if(typeof(config.cls) != 'undefined'){
10356 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10358 if (this.responsive) {
10359 ['xs','sm','md','lg'].map(function(size){
10361 if(typeof(config[size]) == 'undefined'){
10367 if (!config[size]) { // 0 = hidden
10368 // BS 4 '0' is treated as hide that column and below.
10369 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10373 td.cls += ' col-' + size + '-' + config[size] + (
10374 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10384 row.cellObjects = cellObjects;
10392 onBeforeLoad : function()
10401 this.el.select('tbody', true).first().dom.innerHTML = '';
10404 * Show or hide a row.
10405 * @param {Number} rowIndex to show or hide
10406 * @param {Boolean} state hide
10408 setRowVisibility : function(rowIndex, state)
10410 var bt = this.bodyEl.dom;
10412 var rows = this.el.select('tbody > tr', true).elements;
10414 if(typeof(rows[rowIndex]) == 'undefined'){
10417 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10422 getSelectionModel : function(){
10423 if(!this.selModel){
10424 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10426 return this.selModel;
10429 * Render the Roo.bootstrap object from renderder
10431 renderCellObject : function(r)
10435 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10437 var t = r.cfg.render(r.container);
10440 Roo.each(r.cfg.cn, function(c){
10442 container: t.getChildContainer(),
10445 _this.renderCellObject(child);
10450 * get the Row Index from a dom element.
10451 * @param {Roo.Element} row The row to look for
10452 * @returns {Number} the row
10454 getRowIndex : function(row)
10458 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10469 * get the header TH element for columnIndex
10470 * @param {Number} columnIndex
10471 * @returns {Roo.Element}
10473 getHeaderIndex: function(colIndex)
10475 var cols = this.headEl.select('th', true).elements;
10476 return cols[colIndex];
10479 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10480 * @param {domElement} cell to look for
10481 * @returns {Number} the column
10483 getCellIndex : function(cell)
10485 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10487 return parseInt(id[1], 10);
10492 * Returns the grid's underlying element = used by panel.Grid
10493 * @return {Element} The element
10495 getGridEl : function(){
10499 * Forces a resize - used by panel.Grid
10500 * @return {Element} The element
10502 autoSize : function()
10504 //var ctr = Roo.get(this.container.dom.parentElement);
10505 var ctr = Roo.get(this.el.dom);
10507 var thd = this.getGridEl().select('thead',true).first();
10508 var tbd = this.getGridEl().select('tbody', true).first();
10509 var tfd = this.getGridEl().select('tfoot', true).first();
10511 var cw = ctr.getWidth();
10512 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10516 tbd.setWidth(ctr.getWidth());
10517 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10518 // this needs fixing for various usage - currently only hydra job advers I think..
10520 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10522 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10525 cw = Math.max(cw, this.totalWidth);
10526 this.getGridEl().select('tbody tr',true).setWidth(cw);
10529 // resize 'expandable coloumn?
10531 return; // we doe not have a view in this design..
10534 onBodyScroll: function()
10536 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10538 this.headEl.setStyle({
10539 'position' : 'relative',
10540 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10546 var scrollHeight = this.bodyEl.dom.scrollHeight;
10548 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10550 var height = this.bodyEl.getHeight();
10552 if(scrollHeight - height == scrollTop) {
10554 var total = this.ds.getTotalCount();
10556 if(this.footer.cursor + this.footer.pageSize < total){
10558 this.footer.ds.load({
10560 start : this.footer.cursor + this.footer.pageSize,
10561 limit : this.footer.pageSize
10570 onColumnSplitterMoved : function(i, diff)
10572 this.userResized = true;
10574 var cm = this.colModel;
10576 var w = this.getHeaderIndex(i).getWidth() + diff;
10579 cm.setColumnWidth(i, w, true);
10581 //var cid = cm.getColumnId(i); << not used in this version?
10582 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10584 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10585 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10586 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10588 //this.updateSplitters();
10589 //this.layout(); << ??
10590 this.fireEvent("columnresize", i, w);
10592 onHeaderChange : function()
10594 var header = this.renderHeader();
10595 var table = this.el.select('table', true).first();
10597 this.headEl.remove();
10598 this.headEl = table.createChild(header, this.bodyEl, false);
10600 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10601 e.on('click', this.sort, this);
10604 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10605 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10610 onHiddenChange : function(colModel, colIndex, hidden)
10613 this.cm.setHidden()
10614 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10615 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10617 this.CSS.updateRule(thSelector, "display", "");
10618 this.CSS.updateRule(tdSelector, "display", "");
10621 this.CSS.updateRule(thSelector, "display", "none");
10622 this.CSS.updateRule(tdSelector, "display", "none");
10625 // onload calls initCSS()
10626 this.onHeaderChange();
10630 setColumnWidth: function(col_index, width)
10632 // width = "md-2 xs-2..."
10633 if(!this.colModel.config[col_index]) {
10637 var w = width.split(" ");
10639 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10641 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10644 for(var j = 0; j < w.length; j++) {
10650 var size_cls = w[j].split("-");
10652 if(!Number.isInteger(size_cls[1] * 1)) {
10656 if(!this.colModel.config[col_index][size_cls[0]]) {
10660 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10664 h_row[0].classList.replace(
10665 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10666 "col-"+size_cls[0]+"-"+size_cls[1]
10669 for(var i = 0; i < rows.length; i++) {
10671 var size_cls = w[j].split("-");
10673 if(!Number.isInteger(size_cls[1] * 1)) {
10677 if(!this.colModel.config[col_index][size_cls[0]]) {
10681 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10685 rows[i].classList.replace(
10686 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10687 "col-"+size_cls[0]+"-"+size_cls[1]
10691 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10696 // currently only used to find the split on drag..
10697 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10702 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10703 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10712 * @class Roo.bootstrap.TableCell
10713 * @extends Roo.bootstrap.Component
10714 * @children Roo.bootstrap.Component
10715 * @parent Roo.bootstrap.TableRow
10716 * Bootstrap TableCell class
10718 * @cfg {String} html cell contain text
10719 * @cfg {String} cls cell class
10720 * @cfg {String} tag cell tag (td|th) default td
10721 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10722 * @cfg {String} align Aligns the content in a cell
10723 * @cfg {String} axis Categorizes cells
10724 * @cfg {String} bgcolor Specifies the background color of a cell
10725 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10726 * @cfg {Number} colspan Specifies the number of columns a cell should span
10727 * @cfg {String} headers Specifies one or more header cells a cell is related to
10728 * @cfg {Number} height Sets the height of a cell
10729 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10730 * @cfg {Number} rowspan Sets the number of rows a cell should span
10731 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10732 * @cfg {String} valign Vertical aligns the content in a cell
10733 * @cfg {Number} width Specifies the width of a cell
10736 * Create a new TableCell
10737 * @param {Object} config The config object
10740 Roo.bootstrap.TableCell = function(config){
10741 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10744 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10764 getAutoCreate : function(){
10765 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10772 cfg.tag = this.tag;
10785 cfg.align=this.align
10790 if (this.bgcolor) {
10791 cfg.bgcolor=this.bgcolor
10793 if (this.charoff) {
10794 cfg.charoff=this.charoff
10796 if (this.colspan) {
10797 cfg.colspan=this.colspan
10799 if (this.headers) {
10800 cfg.headers=this.headers
10803 cfg.height=this.height
10806 cfg.nowrap=this.nowrap
10808 if (this.rowspan) {
10809 cfg.rowspan=this.rowspan
10812 cfg.scope=this.scope
10815 cfg.valign=this.valign
10818 cfg.width=this.width
10837 * @class Roo.bootstrap.TableRow
10838 * @extends Roo.bootstrap.Component
10839 * @children Roo.bootstrap.TableCell
10840 * @parent Roo.bootstrap.TableBody
10841 * Bootstrap TableRow class
10842 * @cfg {String} cls row class
10843 * @cfg {String} align Aligns the content in a table row
10844 * @cfg {String} bgcolor Specifies a background color for a table row
10845 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10846 * @cfg {String} valign Vertical aligns the content in a table row
10849 * Create a new TableRow
10850 * @param {Object} config The config object
10853 Roo.bootstrap.TableRow = function(config){
10854 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10857 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10865 getAutoCreate : function(){
10866 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10873 cfg.cls = this.cls;
10876 cfg.align = this.align;
10879 cfg.bgcolor = this.bgcolor;
10882 cfg.charoff = this.charoff;
10885 cfg.valign = this.valign;
10903 * @class Roo.bootstrap.TableBody
10904 * @extends Roo.bootstrap.Component
10905 * @children Roo.bootstrap.TableRow
10906 * @parent Roo.bootstrap.Table
10907 * Bootstrap TableBody class
10908 * @cfg {String} cls element class
10909 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10910 * @cfg {String} align Aligns the content inside the element
10911 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10912 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10915 * Create a new TableBody
10916 * @param {Object} config The config object
10919 Roo.bootstrap.TableBody = function(config){
10920 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10923 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10931 getAutoCreate : function(){
10932 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10942 cfg.tag = this.tag;
10946 cfg.align = this.align;
10949 cfg.charoff = this.charoff;
10952 cfg.valign = this.valign;
10959 // initEvents : function()
10962 // if(!this.store){
10966 // this.store = Roo.factory(this.store, Roo.data);
10967 // this.store.on('load', this.onLoad, this);
10969 // this.store.load();
10973 // onLoad: function ()
10975 // this.fireEvent('load', this);
10985 * Ext JS Library 1.1.1
10986 * Copyright(c) 2006-2007, Ext JS, LLC.
10988 * Originally Released Under LGPL - original licence link has changed is not relivant.
10991 * <script type="text/javascript">
10994 // as we use this in bootstrap.
10995 Roo.namespace('Roo.form');
10997 * @class Roo.form.Action
10998 * Internal Class used to handle form actions
11000 * @param {Roo.form.BasicForm} el The form element or its id
11001 * @param {Object} config Configuration options
11006 // define the action interface
11007 Roo.form.Action = function(form, options){
11009 this.options = options || {};
11012 * Client Validation Failed
11015 Roo.form.Action.CLIENT_INVALID = 'client';
11017 * Server Validation Failed
11020 Roo.form.Action.SERVER_INVALID = 'server';
11022 * Connect to Server Failed
11025 Roo.form.Action.CONNECT_FAILURE = 'connect';
11027 * Reading Data from Server Failed
11030 Roo.form.Action.LOAD_FAILURE = 'load';
11032 Roo.form.Action.prototype = {
11034 failureType : undefined,
11035 response : undefined,
11036 result : undefined,
11038 // interface method
11039 run : function(options){
11043 // interface method
11044 success : function(response){
11048 // interface method
11049 handleResponse : function(response){
11053 // default connection failure
11054 failure : function(response){
11056 this.response = response;
11057 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11058 this.form.afterAction(this, false);
11061 processResponse : function(response){
11062 this.response = response;
11063 if(!response.responseText){
11066 this.result = this.handleResponse(response);
11067 return this.result;
11070 // utility functions used internally
11071 getUrl : function(appendParams){
11072 var url = this.options.url || this.form.url || this.form.el.dom.action;
11074 var p = this.getParams();
11076 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11082 getMethod : function(){
11083 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11086 getParams : function(){
11087 var bp = this.form.baseParams;
11088 var p = this.options.params;
11090 if(typeof p == "object"){
11091 p = Roo.urlEncode(Roo.applyIf(p, bp));
11092 }else if(typeof p == 'string' && bp){
11093 p += '&' + Roo.urlEncode(bp);
11096 p = Roo.urlEncode(bp);
11101 createCallback : function(){
11103 success: this.success,
11104 failure: this.failure,
11106 timeout: (this.form.timeout*1000),
11107 upload: this.form.fileUpload ? this.success : undefined
11112 Roo.form.Action.Submit = function(form, options){
11113 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11116 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11119 haveProgress : false,
11120 uploadComplete : false,
11122 // uploadProgress indicator.
11123 uploadProgress : function()
11125 if (!this.form.progressUrl) {
11129 if (!this.haveProgress) {
11130 Roo.MessageBox.progress("Uploading", "Uploading");
11132 if (this.uploadComplete) {
11133 Roo.MessageBox.hide();
11137 this.haveProgress = true;
11139 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11141 var c = new Roo.data.Connection();
11143 url : this.form.progressUrl,
11148 success : function(req){
11149 //console.log(data);
11153 rdata = Roo.decode(req.responseText)
11155 Roo.log("Invalid data from server..");
11159 if (!rdata || !rdata.success) {
11161 Roo.MessageBox.alert(Roo.encode(rdata));
11164 var data = rdata.data;
11166 if (this.uploadComplete) {
11167 Roo.MessageBox.hide();
11172 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11173 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11176 this.uploadProgress.defer(2000,this);
11179 failure: function(data) {
11180 Roo.log('progress url failed ');
11191 // run get Values on the form, so it syncs any secondary forms.
11192 this.form.getValues();
11194 var o = this.options;
11195 var method = this.getMethod();
11196 var isPost = method == 'POST';
11197 if(o.clientValidation === false || this.form.isValid()){
11199 if (this.form.progressUrl) {
11200 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11201 (new Date() * 1) + '' + Math.random());
11206 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11207 form:this.form.el.dom,
11208 url:this.getUrl(!isPost),
11210 params:isPost ? this.getParams() : null,
11211 isUpload: this.form.fileUpload,
11212 formData : this.form.formData
11215 this.uploadProgress();
11217 }else if (o.clientValidation !== false){ // client validation failed
11218 this.failureType = Roo.form.Action.CLIENT_INVALID;
11219 this.form.afterAction(this, false);
11223 success : function(response)
11225 this.uploadComplete= true;
11226 if (this.haveProgress) {
11227 Roo.MessageBox.hide();
11231 var result = this.processResponse(response);
11232 if(result === true || result.success){
11233 this.form.afterAction(this, true);
11237 this.form.markInvalid(result.errors);
11238 this.failureType = Roo.form.Action.SERVER_INVALID;
11240 this.form.afterAction(this, false);
11242 failure : function(response)
11244 this.uploadComplete= true;
11245 if (this.haveProgress) {
11246 Roo.MessageBox.hide();
11249 this.response = response;
11250 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11251 this.form.afterAction(this, false);
11254 handleResponse : function(response){
11255 if(this.form.errorReader){
11256 var rs = this.form.errorReader.read(response);
11259 for(var i = 0, len = rs.records.length; i < len; i++) {
11260 var r = rs.records[i];
11261 errors[i] = r.data;
11264 if(errors.length < 1){
11268 success : rs.success,
11274 ret = Roo.decode(response.responseText);
11278 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11288 Roo.form.Action.Load = function(form, options){
11289 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11290 this.reader = this.form.reader;
11293 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11298 Roo.Ajax.request(Roo.apply(
11299 this.createCallback(), {
11300 method:this.getMethod(),
11301 url:this.getUrl(false),
11302 params:this.getParams()
11306 success : function(response){
11308 var result = this.processResponse(response);
11309 if(result === true || !result.success || !result.data){
11310 this.failureType = Roo.form.Action.LOAD_FAILURE;
11311 this.form.afterAction(this, false);
11314 this.form.clearInvalid();
11315 this.form.setValues(result.data);
11316 this.form.afterAction(this, true);
11319 handleResponse : function(response){
11320 if(this.form.reader){
11321 var rs = this.form.reader.read(response);
11322 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11324 success : rs.success,
11328 return Roo.decode(response.responseText);
11332 Roo.form.Action.ACTION_TYPES = {
11333 'load' : Roo.form.Action.Load,
11334 'submit' : Roo.form.Action.Submit
11343 * @class Roo.bootstrap.Form
11344 * @extends Roo.bootstrap.Component
11345 * @children Roo.bootstrap.Component
11346 * Bootstrap Form class
11347 * @cfg {String} method GET | POST (default POST)
11348 * @cfg {String} labelAlign top | left (default top)
11349 * @cfg {String} align left | right - for navbars
11350 * @cfg {Boolean} loadMask load mask when submit (default true)
11354 * Create a new Form
11355 * @param {Object} config The config object
11359 Roo.bootstrap.Form = function(config){
11361 Roo.bootstrap.Form.superclass.constructor.call(this, config);
11363 Roo.bootstrap.Form.popover.apply();
11367 * @event clientvalidation
11368 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11369 * @param {Form} this
11370 * @param {Boolean} valid true if the form has passed client-side validation
11372 clientvalidation: true,
11374 * @event beforeaction
11375 * Fires before any action is performed. Return false to cancel the action.
11376 * @param {Form} this
11377 * @param {Action} action The action to be performed
11379 beforeaction: true,
11381 * @event actionfailed
11382 * Fires when an action fails.
11383 * @param {Form} this
11384 * @param {Action} action The action that failed
11386 actionfailed : true,
11388 * @event actioncomplete
11389 * Fires when an action is completed.
11390 * @param {Form} this
11391 * @param {Action} action The action that completed
11393 actioncomplete : true
11397 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
11400 * @cfg {String} method
11401 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11405 * @cfg {String} url
11406 * The URL to use for form actions if one isn't supplied in the action options.
11409 * @cfg {Boolean} fileUpload
11410 * Set to true if this form is a file upload.
11414 * @cfg {Object} baseParams
11415 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11419 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11423 * @cfg {Sting} align (left|right) for navbar forms
11428 activeAction : null,
11431 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11432 * element by passing it or its id or mask the form itself by passing in true.
11435 waitMsgTarget : false,
11440 * @cfg {Boolean} errorMask (true|false) default false
11445 * @cfg {Number} maskOffset Default 100
11450 * @cfg {Boolean} maskBody
11454 getAutoCreate : function(){
11458 method : this.method || 'POST',
11459 id : this.id || Roo.id(),
11462 if (this.parent().xtype.match(/^Nav/)) {
11463 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11467 if (this.labelAlign == 'left' ) {
11468 cfg.cls += ' form-horizontal';
11474 initEvents : function()
11476 this.el.on('submit', this.onSubmit, this);
11477 // this was added as random key presses on the form where triggering form submit.
11478 this.el.on('keypress', function(e) {
11479 if (e.getCharCode() != 13) {
11482 // we might need to allow it for textareas.. and some other items.
11483 // check e.getTarget().
11485 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11489 Roo.log("keypress blocked");
11491 e.preventDefault();
11497 onSubmit : function(e){
11502 * Returns true if client-side validation on the form is successful.
11505 isValid : function(){
11506 var items = this.getItems();
11508 var target = false;
11510 items.each(function(f){
11516 Roo.log('invalid field: ' + f.name);
11520 if(!target && f.el.isVisible(true)){
11526 if(this.errorMask && !valid){
11527 Roo.bootstrap.Form.popover.mask(this, target);
11534 * Returns true if any fields in this form have changed since their original load.
11537 isDirty : function(){
11539 var items = this.getItems();
11540 items.each(function(f){
11550 * Performs a predefined action (submit or load) or custom actions you define on this form.
11551 * @param {String} actionName The name of the action type
11552 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11553 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11554 * accept other config options):
11556 Property Type Description
11557 ---------------- --------------- ----------------------------------------------------------------------------------
11558 url String The url for the action (defaults to the form's url)
11559 method String The form method to use (defaults to the form's method, or POST if not defined)
11560 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11561 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11562 validate the form on the client (defaults to false)
11564 * @return {BasicForm} this
11566 doAction : function(action, options){
11567 if(typeof action == 'string'){
11568 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11570 if(this.fireEvent('beforeaction', this, action) !== false){
11571 this.beforeAction(action);
11572 action.run.defer(100, action);
11578 beforeAction : function(action){
11579 var o = action.options;
11584 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11586 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11589 // not really supported yet.. ??
11591 //if(this.waitMsgTarget === true){
11592 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11593 //}else if(this.waitMsgTarget){
11594 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11595 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11597 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11603 afterAction : function(action, success){
11604 this.activeAction = null;
11605 var o = action.options;
11610 Roo.get(document.body).unmask();
11616 //if(this.waitMsgTarget === true){
11617 // this.el.unmask();
11618 //}else if(this.waitMsgTarget){
11619 // this.waitMsgTarget.unmask();
11621 // Roo.MessageBox.updateProgress(1);
11622 // Roo.MessageBox.hide();
11629 Roo.callback(o.success, o.scope, [this, action]);
11630 this.fireEvent('actioncomplete', this, action);
11634 // failure condition..
11635 // we have a scenario where updates need confirming.
11636 // eg. if a locking scenario exists..
11637 // we look for { errors : { needs_confirm : true }} in the response.
11639 (typeof(action.result) != 'undefined') &&
11640 (typeof(action.result.errors) != 'undefined') &&
11641 (typeof(action.result.errors.needs_confirm) != 'undefined')
11644 Roo.log("not supported yet");
11647 Roo.MessageBox.confirm(
11648 "Change requires confirmation",
11649 action.result.errorMsg,
11654 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11664 Roo.callback(o.failure, o.scope, [this, action]);
11665 // show an error message if no failed handler is set..
11666 if (!this.hasListener('actionfailed')) {
11667 Roo.log("need to add dialog support");
11669 Roo.MessageBox.alert("Error",
11670 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11671 action.result.errorMsg :
11672 "Saving Failed, please check your entries or try again"
11677 this.fireEvent('actionfailed', this, action);
11682 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11683 * @param {String} id The value to search for
11686 findField : function(id){
11687 var items = this.getItems();
11688 var field = items.get(id);
11690 items.each(function(f){
11691 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11698 return field || null;
11701 * Mark fields in this form invalid in bulk.
11702 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11703 * @return {BasicForm} this
11705 markInvalid : function(errors){
11706 if(errors instanceof Array){
11707 for(var i = 0, len = errors.length; i < len; i++){
11708 var fieldError = errors[i];
11709 var f = this.findField(fieldError.id);
11711 f.markInvalid(fieldError.msg);
11717 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11718 field.markInvalid(errors[id]);
11722 //Roo.each(this.childForms || [], function (f) {
11723 // f.markInvalid(errors);
11730 * Set values for fields in this form in bulk.
11731 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11732 * @return {BasicForm} this
11734 setValues : function(values){
11735 if(values instanceof Array){ // array of objects
11736 for(var i = 0, len = values.length; i < len; i++){
11738 var f = this.findField(v.id);
11740 f.setValue(v.value);
11741 if(this.trackResetOnLoad){
11742 f.originalValue = f.getValue();
11746 }else{ // object hash
11749 if(typeof values[id] != 'function' && (field = this.findField(id))){
11751 if (field.setFromData &&
11752 field.valueField &&
11753 field.displayField &&
11754 // combos' with local stores can
11755 // be queried via setValue()
11756 // to set their value..
11757 (field.store && !field.store.isLocal)
11761 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11762 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11763 field.setFromData(sd);
11765 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11767 field.setFromData(values);
11770 field.setValue(values[id]);
11774 if(this.trackResetOnLoad){
11775 field.originalValue = field.getValue();
11781 //Roo.each(this.childForms || [], function (f) {
11782 // f.setValues(values);
11789 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11790 * they are returned as an array.
11791 * @param {Boolean} asString
11794 getValues : function(asString){
11795 //if (this.childForms) {
11796 // copy values from the child forms
11797 // Roo.each(this.childForms, function (f) {
11798 // this.setValues(f.getValues());
11804 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11805 if(asString === true){
11808 return Roo.urlDecode(fs);
11812 * Returns the fields in this form as an object with key/value pairs.
11813 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11816 getFieldValues : function(with_hidden)
11818 var items = this.getItems();
11820 items.each(function(f){
11822 if (!f.getName()) {
11826 var v = f.getValue();
11828 if (f.inputType =='radio') {
11829 if (typeof(ret[f.getName()]) == 'undefined') {
11830 ret[f.getName()] = ''; // empty..
11833 if (!f.el.dom.checked) {
11837 v = f.el.dom.value;
11841 if(f.xtype == 'MoneyField'){
11842 ret[f.currencyName] = f.getCurrency();
11845 // not sure if this supported any more..
11846 if ((typeof(v) == 'object') && f.getRawValue) {
11847 v = f.getRawValue() ; // dates..
11849 // combo boxes where name != hiddenName...
11850 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11851 ret[f.name] = f.getRawValue();
11853 ret[f.getName()] = v;
11860 * Clears all invalid messages in this form.
11861 * @return {BasicForm} this
11863 clearInvalid : function(){
11864 var items = this.getItems();
11866 items.each(function(f){
11874 * Resets this form.
11875 * @return {BasicForm} this
11877 reset : function(){
11878 var items = this.getItems();
11879 items.each(function(f){
11883 Roo.each(this.childForms || [], function (f) {
11891 getItems : function()
11893 var r=new Roo.util.MixedCollection(false, function(o){
11894 return o.id || (o.id = Roo.id());
11896 var iter = function(el) {
11903 Roo.each(el.items,function(e) {
11912 hideFields : function(items)
11914 Roo.each(items, function(i){
11916 var f = this.findField(i);
11927 showFields : function(items)
11929 Roo.each(items, function(i){
11931 var f = this.findField(i);
11944 Roo.apply(Roo.bootstrap.Form, {
11960 intervalID : false,
11966 if(this.isApplied){
11971 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11972 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11973 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11974 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11977 this.maskEl.top.enableDisplayMode("block");
11978 this.maskEl.left.enableDisplayMode("block");
11979 this.maskEl.bottom.enableDisplayMode("block");
11980 this.maskEl.right.enableDisplayMode("block");
11982 this.toolTip = new Roo.bootstrap.Tooltip({
11983 cls : 'roo-form-error-popover',
11985 'left' : ['r-l', [-2,0], 'right'],
11986 'right' : ['l-r', [2,0], 'left'],
11987 'bottom' : ['tl-bl', [0,2], 'top'],
11988 'top' : [ 'bl-tl', [0,-2], 'bottom']
11992 this.toolTip.render(Roo.get(document.body));
11994 this.toolTip.el.enableDisplayMode("block");
11996 Roo.get(document.body).on('click', function(){
12000 Roo.get(document.body).on('touchstart', function(){
12004 this.isApplied = true
12007 mask : function(form, target)
12011 this.target = target;
12013 if(!this.form.errorMask || !target.el){
12017 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12019 Roo.log(scrollable);
12021 var ot = this.target.el.calcOffsetsTo(scrollable);
12023 var scrollTo = ot[1] - this.form.maskOffset;
12025 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12027 scrollable.scrollTo('top', scrollTo);
12029 var box = this.target.el.getBox();
12031 var zIndex = Roo.bootstrap.Modal.zIndex++;
12034 this.maskEl.top.setStyle('position', 'absolute');
12035 this.maskEl.top.setStyle('z-index', zIndex);
12036 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12037 this.maskEl.top.setLeft(0);
12038 this.maskEl.top.setTop(0);
12039 this.maskEl.top.show();
12041 this.maskEl.left.setStyle('position', 'absolute');
12042 this.maskEl.left.setStyle('z-index', zIndex);
12043 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12044 this.maskEl.left.setLeft(0);
12045 this.maskEl.left.setTop(box.y - this.padding);
12046 this.maskEl.left.show();
12048 this.maskEl.bottom.setStyle('position', 'absolute');
12049 this.maskEl.bottom.setStyle('z-index', zIndex);
12050 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12051 this.maskEl.bottom.setLeft(0);
12052 this.maskEl.bottom.setTop(box.bottom + this.padding);
12053 this.maskEl.bottom.show();
12055 this.maskEl.right.setStyle('position', 'absolute');
12056 this.maskEl.right.setStyle('z-index', zIndex);
12057 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12058 this.maskEl.right.setLeft(box.right + this.padding);
12059 this.maskEl.right.setTop(box.y - this.padding);
12060 this.maskEl.right.show();
12062 this.toolTip.bindEl = this.target.el;
12064 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12066 var tip = this.target.blankText;
12068 if(this.target.getValue() !== '' ) {
12070 if (this.target.invalidText.length) {
12071 tip = this.target.invalidText;
12072 } else if (this.target.regexText.length){
12073 tip = this.target.regexText;
12077 this.toolTip.show(tip);
12079 this.intervalID = window.setInterval(function() {
12080 Roo.bootstrap.Form.popover.unmask();
12083 window.onwheel = function(){ return false;};
12085 (function(){ this.isMasked = true; }).defer(500, this);
12089 unmask : function()
12091 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12095 this.maskEl.top.setStyle('position', 'absolute');
12096 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12097 this.maskEl.top.hide();
12099 this.maskEl.left.setStyle('position', 'absolute');
12100 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12101 this.maskEl.left.hide();
12103 this.maskEl.bottom.setStyle('position', 'absolute');
12104 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12105 this.maskEl.bottom.hide();
12107 this.maskEl.right.setStyle('position', 'absolute');
12108 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12109 this.maskEl.right.hide();
12111 this.toolTip.hide();
12113 this.toolTip.el.hide();
12115 window.onwheel = function(){ return true;};
12117 if(this.intervalID){
12118 window.clearInterval(this.intervalID);
12119 this.intervalID = false;
12122 this.isMasked = false;
12132 * Ext JS Library 1.1.1
12133 * Copyright(c) 2006-2007, Ext JS, LLC.
12135 * Originally Released Under LGPL - original licence link has changed is not relivant.
12138 * <script type="text/javascript">
12141 * @class Roo.form.VTypes
12142 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12145 Roo.form.VTypes = function(){
12146 // closure these in so they are only created once.
12147 var alpha = /^[a-zA-Z_]+$/;
12148 var alphanum = /^[a-zA-Z0-9_]+$/;
12149 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12150 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12152 // All these messages and functions are configurable
12155 * The function used to validate email addresses
12156 * @param {String} value The email address
12158 'email' : function(v){
12159 return email.test(v);
12162 * The error text to display when the email validation function returns false
12165 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12167 * The keystroke filter mask to be applied on email input
12170 'emailMask' : /[a-z0-9_\.\-@]/i,
12173 * The function used to validate URLs
12174 * @param {String} value The URL
12176 'url' : function(v){
12177 return url.test(v);
12180 * The error text to display when the url validation function returns false
12183 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12186 * The function used to validate alpha values
12187 * @param {String} value The value
12189 'alpha' : function(v){
12190 return alpha.test(v);
12193 * The error text to display when the alpha validation function returns false
12196 'alphaText' : 'This field should only contain letters and _',
12198 * The keystroke filter mask to be applied on alpha input
12201 'alphaMask' : /[a-z_]/i,
12204 * The function used to validate alphanumeric values
12205 * @param {String} value The value
12207 'alphanum' : function(v){
12208 return alphanum.test(v);
12211 * The error text to display when the alphanumeric validation function returns false
12214 'alphanumText' : 'This field should only contain letters, numbers and _',
12216 * The keystroke filter mask to be applied on alphanumeric input
12219 'alphanumMask' : /[a-z0-9_]/i
12229 * @class Roo.bootstrap.Input
12230 * @extends Roo.bootstrap.Component
12231 * Bootstrap Input class
12232 * @cfg {Boolean} disabled is it disabled
12233 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12234 * @cfg {String} name name of the input
12235 * @cfg {string} fieldLabel - the label associated
12236 * @cfg {string} placeholder - placeholder to put in text.
12237 * @cfg {string} before - input group add on before
12238 * @cfg {string} after - input group add on after
12239 * @cfg {string} size - (lg|sm) or leave empty..
12240 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12241 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12242 * @cfg {Number} md colspan out of 12 for computer-sized screens
12243 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12244 * @cfg {string} value default value of the input
12245 * @cfg {Number} labelWidth set the width of label
12246 * @cfg {Number} labellg set the width of label (1-12)
12247 * @cfg {Number} labelmd set the width of label (1-12)
12248 * @cfg {Number} labelsm set the width of label (1-12)
12249 * @cfg {Number} labelxs set the width of label (1-12)
12250 * @cfg {String} labelAlign (top|left)
12251 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12252 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12253 * @cfg {String} indicatorpos (left|right) default left
12254 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12255 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12256 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12257 * @cfg {Roo.bootstrap.Button} before Button to show before
12258 * @cfg {Roo.bootstrap.Button} afterButton to show before
12259 * @cfg {String} align (left|center|right) Default left
12260 * @cfg {Boolean} forceFeedback (true|false) Default false
12263 * Create a new Input
12264 * @param {Object} config The config object
12267 Roo.bootstrap.Input = function(config){
12269 Roo.bootstrap.Input.superclass.constructor.call(this, config);
12274 * Fires when this field receives input focus.
12275 * @param {Roo.form.Field} this
12280 * Fires when this field loses input focus.
12281 * @param {Roo.form.Field} this
12285 * @event specialkey
12286 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12287 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12288 * @param {Roo.form.Field} this
12289 * @param {Roo.EventObject} e The event object
12294 * Fires just before the field blurs if the field value has changed.
12295 * @param {Roo.form.Field} this
12296 * @param {Mixed} newValue The new value
12297 * @param {Mixed} oldValue The original value
12302 * Fires after the field has been marked as invalid.
12303 * @param {Roo.form.Field} this
12304 * @param {String} msg The validation message
12309 * Fires after the field has been validated with no errors.
12310 * @param {Roo.form.Field} this
12315 * Fires after the key up
12316 * @param {Roo.form.Field} this
12317 * @param {Roo.EventObject} e The event Object
12322 * Fires after the user pastes into input
12323 * @param {Roo.form.Field} this
12324 * @param {Roo.EventObject} e The event Object
12330 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
12332 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12333 automatic validation (defaults to "keyup").
12335 validationEvent : "keyup",
12337 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12339 validateOnBlur : true,
12341 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12343 validationDelay : 250,
12345 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12347 focusClass : "x-form-focus", // not needed???
12351 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12353 invalidClass : "has-warning",
12356 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12358 validClass : "has-success",
12361 * @cfg {Boolean} hasFeedback (true|false) default true
12363 hasFeedback : true,
12366 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12368 invalidFeedbackClass : "glyphicon-warning-sign",
12371 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12373 validFeedbackClass : "glyphicon-ok",
12376 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12378 selectOnFocus : false,
12381 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12385 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12390 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12392 disableKeyFilter : false,
12395 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12399 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12403 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12405 blankText : "Please complete this mandatory field",
12408 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12412 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12414 maxLength : Number.MAX_VALUE,
12416 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12418 minLengthText : "The minimum length for this field is {0}",
12420 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12422 maxLengthText : "The maximum length for this field is {0}",
12426 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12427 * If available, this function will be called only after the basic validators all return true, and will be passed the
12428 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12432 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12433 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12434 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12438 * @cfg {String} regexText -- Depricated - use Invalid Text
12443 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12449 autocomplete: false,
12453 inputType : 'text',
12456 placeholder: false,
12461 preventMark: false,
12462 isFormField : true,
12465 labelAlign : false,
12468 formatedValue : false,
12469 forceFeedback : false,
12471 indicatorpos : 'left',
12481 parentLabelAlign : function()
12484 while (parent.parent()) {
12485 parent = parent.parent();
12486 if (typeof(parent.labelAlign) !='undefined') {
12487 return parent.labelAlign;
12494 getAutoCreate : function()
12496 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12502 if(this.inputType != 'hidden'){
12503 cfg.cls = 'form-group' //input-group
12509 type : this.inputType,
12510 value : this.value,
12511 cls : 'form-control',
12512 placeholder : this.placeholder || '',
12513 autocomplete : this.autocomplete || 'new-password'
12515 if (this.inputType == 'file') {
12516 input.style = 'overflow:hidden'; // why not in CSS?
12519 if(this.capture.length){
12520 input.capture = this.capture;
12523 if(this.accept.length){
12524 input.accept = this.accept + "/*";
12528 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12531 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12532 input.maxLength = this.maxLength;
12535 if (this.disabled) {
12536 input.disabled=true;
12539 if (this.readOnly) {
12540 input.readonly=true;
12544 input.name = this.name;
12548 input.cls += ' input-' + this.size;
12552 ['xs','sm','md','lg'].map(function(size){
12553 if (settings[size]) {
12554 cfg.cls += ' col-' + size + '-' + settings[size];
12558 var inputblock = input;
12562 cls: 'glyphicon form-control-feedback'
12565 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12568 cls : 'has-feedback',
12576 if (this.before || this.after) {
12579 cls : 'input-group',
12583 if (this.before && typeof(this.before) == 'string') {
12585 inputblock.cn.push({
12587 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12591 if (this.before && typeof(this.before) == 'object') {
12592 this.before = Roo.factory(this.before);
12594 inputblock.cn.push({
12596 cls : 'roo-input-before input-group-prepend input-group-' +
12597 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12601 inputblock.cn.push(input);
12603 if (this.after && typeof(this.after) == 'string') {
12604 inputblock.cn.push({
12606 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12610 if (this.after && typeof(this.after) == 'object') {
12611 this.after = Roo.factory(this.after);
12613 inputblock.cn.push({
12615 cls : 'roo-input-after input-group-append input-group-' +
12616 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12620 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12621 inputblock.cls += ' has-feedback';
12622 inputblock.cn.push(feedback);
12627 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12628 tooltip : 'This field is required'
12630 if (this.allowBlank ) {
12631 indicator.style = this.allowBlank ? ' display:none' : '';
12633 if (align ==='left' && this.fieldLabel.length) {
12635 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12642 cls : 'control-label col-form-label',
12643 html : this.fieldLabel
12654 var labelCfg = cfg.cn[1];
12655 var contentCfg = cfg.cn[2];
12657 if(this.indicatorpos == 'right'){
12662 cls : 'control-label col-form-label',
12666 html : this.fieldLabel
12680 labelCfg = cfg.cn[0];
12681 contentCfg = cfg.cn[1];
12685 if(this.labelWidth > 12){
12686 labelCfg.style = "width: " + this.labelWidth + 'px';
12689 if(this.labelWidth < 13 && this.labelmd == 0){
12690 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12693 if(this.labellg > 0){
12694 labelCfg.cls += ' col-lg-' + this.labellg;
12695 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12698 if(this.labelmd > 0){
12699 labelCfg.cls += ' col-md-' + this.labelmd;
12700 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12703 if(this.labelsm > 0){
12704 labelCfg.cls += ' col-sm-' + this.labelsm;
12705 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12708 if(this.labelxs > 0){
12709 labelCfg.cls += ' col-xs-' + this.labelxs;
12710 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12714 } else if ( this.fieldLabel.length) {
12721 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12722 tooltip : 'This field is required',
12723 style : this.allowBlank ? ' display:none' : ''
12727 //cls : 'input-group-addon',
12728 html : this.fieldLabel
12736 if(this.indicatorpos == 'right'){
12741 //cls : 'input-group-addon',
12742 html : this.fieldLabel
12747 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12748 tooltip : 'This field is required',
12749 style : this.allowBlank ? ' display:none' : ''
12769 if (this.parentType === 'Navbar' && this.parent().bar) {
12770 cfg.cls += ' navbar-form';
12773 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12774 // on BS4 we do this only if not form
12775 cfg.cls += ' navbar-form';
12783 * return the real input element.
12785 inputEl: function ()
12787 return this.el.select('input.form-control',true).first();
12790 tooltipEl : function()
12792 return this.inputEl();
12795 indicatorEl : function()
12797 if (Roo.bootstrap.version == 4) {
12798 return false; // not enabled in v4 yet.
12801 var indicator = this.el.select('i.roo-required-indicator',true).first();
12811 setDisabled : function(v)
12813 var i = this.inputEl().dom;
12815 i.removeAttribute('disabled');
12819 i.setAttribute('disabled','true');
12821 initEvents : function()
12824 this.inputEl().on("keydown" , this.fireKey, this);
12825 this.inputEl().on("focus", this.onFocus, this);
12826 this.inputEl().on("blur", this.onBlur, this);
12828 this.inputEl().relayEvent('keyup', this);
12829 this.inputEl().relayEvent('paste', this);
12831 this.indicator = this.indicatorEl();
12833 if(this.indicator){
12834 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12837 // reference to original value for reset
12838 this.originalValue = this.getValue();
12839 //Roo.form.TextField.superclass.initEvents.call(this);
12840 if(this.validationEvent == 'keyup'){
12841 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12842 this.inputEl().on('keyup', this.filterValidation, this);
12844 else if(this.validationEvent !== false){
12845 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12848 if(this.selectOnFocus){
12849 this.on("focus", this.preFocus, this);
12852 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12853 this.inputEl().on("keypress", this.filterKeys, this);
12855 this.inputEl().relayEvent('keypress', this);
12858 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12859 this.el.on("click", this.autoSize, this);
12862 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12863 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12866 if (typeof(this.before) == 'object') {
12867 this.before.render(this.el.select('.roo-input-before',true).first());
12869 if (typeof(this.after) == 'object') {
12870 this.after.render(this.el.select('.roo-input-after',true).first());
12873 this.inputEl().on('change', this.onChange, this);
12876 filterValidation : function(e){
12877 if(!e.isNavKeyPress()){
12878 this.validationTask.delay(this.validationDelay);
12882 * Validates the field value
12883 * @return {Boolean} True if the value is valid, else false
12885 validate : function(){
12886 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12887 if(this.disabled || this.validateValue(this.getRawValue())){
12892 this.markInvalid();
12898 * Validates a value according to the field's validation rules and marks the field as invalid
12899 * if the validation fails
12900 * @param {Mixed} value The value to validate
12901 * @return {Boolean} True if the value is valid, else false
12903 validateValue : function(value)
12905 if(this.getVisibilityEl().hasClass('hidden')){
12909 if(value.length < 1) { // if it's blank
12910 if(this.allowBlank){
12916 if(value.length < this.minLength){
12919 if(value.length > this.maxLength){
12923 var vt = Roo.form.VTypes;
12924 if(!vt[this.vtype](value, this)){
12928 if(typeof this.validator == "function"){
12929 var msg = this.validator(value);
12933 if (typeof(msg) == 'string') {
12934 this.invalidText = msg;
12938 if(this.regex && !this.regex.test(value)){
12946 fireKey : function(e){
12947 //Roo.log('field ' + e.getKey());
12948 if(e.isNavKeyPress()){
12949 this.fireEvent("specialkey", this, e);
12952 focus : function (selectText){
12954 this.inputEl().focus();
12955 if(selectText === true){
12956 this.inputEl().dom.select();
12962 onFocus : function(){
12963 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12964 // this.el.addClass(this.focusClass);
12966 if(!this.hasFocus){
12967 this.hasFocus = true;
12968 this.startValue = this.getValue();
12969 this.fireEvent("focus", this);
12973 beforeBlur : Roo.emptyFn,
12977 onBlur : function(){
12979 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12980 //this.el.removeClass(this.focusClass);
12982 this.hasFocus = false;
12983 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12986 var v = this.getValue();
12987 if(String(v) !== String(this.startValue)){
12988 this.fireEvent('change', this, v, this.startValue);
12990 this.fireEvent("blur", this);
12993 onChange : function(e)
12995 var v = this.getValue();
12996 if(String(v) !== String(this.startValue)){
12997 this.fireEvent('change', this, v, this.startValue);
13003 * Resets the current field value to the originally loaded value and clears any validation messages
13005 reset : function(){
13006 this.setValue(this.originalValue);
13010 * Returns the name of the field
13011 * @return {Mixed} name The name field
13013 getName: function(){
13017 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13018 * @return {Mixed} value The field value
13020 getValue : function(){
13022 var v = this.inputEl().getValue();
13027 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13028 * @return {Mixed} value The field value
13030 getRawValue : function(){
13031 var v = this.inputEl().getValue();
13037 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13038 * @param {Mixed} value The value to set
13040 setRawValue : function(v){
13041 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13044 selectText : function(start, end){
13045 var v = this.getRawValue();
13047 start = start === undefined ? 0 : start;
13048 end = end === undefined ? v.length : end;
13049 var d = this.inputEl().dom;
13050 if(d.setSelectionRange){
13051 d.setSelectionRange(start, end);
13052 }else if(d.createTextRange){
13053 var range = d.createTextRange();
13054 range.moveStart("character", start);
13055 range.moveEnd("character", v.length-end);
13062 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13063 * @param {Mixed} value The value to set
13065 setValue : function(v){
13068 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13074 processValue : function(value){
13075 if(this.stripCharsRe){
13076 var newValue = value.replace(this.stripCharsRe, '');
13077 if(newValue !== value){
13078 this.setRawValue(newValue);
13085 preFocus : function(){
13087 if(this.selectOnFocus){
13088 this.inputEl().dom.select();
13091 filterKeys : function(e){
13092 var k = e.getKey();
13093 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13096 var c = e.getCharCode(), cc = String.fromCharCode(c);
13097 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13100 if(!this.maskRe.test(cc)){
13105 * Clear any invalid styles/messages for this field
13107 clearInvalid : function(){
13109 if(!this.el || this.preventMark){ // not rendered
13114 this.el.removeClass([this.invalidClass, 'is-invalid']);
13116 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13118 var feedback = this.el.select('.form-control-feedback', true).first();
13121 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13126 if(this.indicator){
13127 this.indicator.removeClass('visible');
13128 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13131 this.fireEvent('valid', this);
13135 * Mark this field as valid
13137 markValid : function()
13139 if(!this.el || this.preventMark){ // not rendered...
13143 this.el.removeClass([this.invalidClass, this.validClass]);
13144 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13146 var feedback = this.el.select('.form-control-feedback', true).first();
13149 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13152 if(this.indicator){
13153 this.indicator.removeClass('visible');
13154 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13162 if(this.allowBlank && !this.getRawValue().length){
13165 if (Roo.bootstrap.version == 3) {
13166 this.el.addClass(this.validClass);
13168 this.inputEl().addClass('is-valid');
13171 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13173 var feedback = this.el.select('.form-control-feedback', true).first();
13176 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13177 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13182 this.fireEvent('valid', this);
13186 * Mark this field as invalid
13187 * @param {String} msg The validation message
13189 markInvalid : function(msg)
13191 if(!this.el || this.preventMark){ // not rendered
13195 this.el.removeClass([this.invalidClass, this.validClass]);
13196 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13198 var feedback = this.el.select('.form-control-feedback', true).first();
13201 this.el.select('.form-control-feedback', true).first().removeClass(
13202 [this.invalidFeedbackClass, this.validFeedbackClass]);
13209 if(this.allowBlank && !this.getRawValue().length){
13213 if(this.indicator){
13214 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13215 this.indicator.addClass('visible');
13217 if (Roo.bootstrap.version == 3) {
13218 this.el.addClass(this.invalidClass);
13220 this.inputEl().addClass('is-invalid');
13225 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13227 var feedback = this.el.select('.form-control-feedback', true).first();
13230 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13232 if(this.getValue().length || this.forceFeedback){
13233 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13240 this.fireEvent('invalid', this, msg);
13243 SafariOnKeyDown : function(event)
13245 // this is a workaround for a password hang bug on chrome/ webkit.
13246 if (this.inputEl().dom.type != 'password') {
13250 var isSelectAll = false;
13252 if(this.inputEl().dom.selectionEnd > 0){
13253 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13255 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13256 event.preventDefault();
13261 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13263 event.preventDefault();
13264 // this is very hacky as keydown always get's upper case.
13266 var cc = String.fromCharCode(event.getCharCode());
13267 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13271 adjustWidth : function(tag, w){
13272 tag = tag.toLowerCase();
13273 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13274 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13275 if(tag == 'input'){
13278 if(tag == 'textarea'){
13281 }else if(Roo.isOpera){
13282 if(tag == 'input'){
13285 if(tag == 'textarea'){
13293 setFieldLabel : function(v)
13295 if(!this.rendered){
13299 if(this.indicatorEl()){
13300 var ar = this.el.select('label > span',true);
13302 if (ar.elements.length) {
13303 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13304 this.fieldLabel = v;
13308 var br = this.el.select('label',true);
13310 if(br.elements.length) {
13311 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13312 this.fieldLabel = v;
13316 Roo.log('Cannot Found any of label > span || label in input');
13320 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13321 this.fieldLabel = v;
13336 * @class Roo.bootstrap.TextArea
13337 * @extends Roo.bootstrap.Input
13338 * Bootstrap TextArea class
13339 * @cfg {Number} cols Specifies the visible width of a text area
13340 * @cfg {Number} rows Specifies the visible number of lines in a text area
13341 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13342 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13343 * @cfg {string} html text
13346 * Create a new TextArea
13347 * @param {Object} config The config object
13350 Roo.bootstrap.TextArea = function(config){
13351 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
13355 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
13365 getAutoCreate : function(){
13367 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13373 if(this.inputType != 'hidden'){
13374 cfg.cls = 'form-group' //input-group
13382 value : this.value || '',
13383 html: this.html || '',
13384 cls : 'form-control',
13385 placeholder : this.placeholder || ''
13389 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13390 input.maxLength = this.maxLength;
13394 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13398 input.cols = this.cols;
13401 if (this.readOnly) {
13402 input.readonly = true;
13406 input.name = this.name;
13410 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13414 ['xs','sm','md','lg'].map(function(size){
13415 if (settings[size]) {
13416 cfg.cls += ' col-' + size + '-' + settings[size];
13420 var inputblock = input;
13422 if(this.hasFeedback && !this.allowBlank){
13426 cls: 'glyphicon form-control-feedback'
13430 cls : 'has-feedback',
13439 if (this.before || this.after) {
13442 cls : 'input-group',
13446 inputblock.cn.push({
13448 cls : 'input-group-addon',
13453 inputblock.cn.push(input);
13455 if(this.hasFeedback && !this.allowBlank){
13456 inputblock.cls += ' has-feedback';
13457 inputblock.cn.push(feedback);
13461 inputblock.cn.push({
13463 cls : 'input-group-addon',
13470 if (align ==='left' && this.fieldLabel.length) {
13475 cls : 'control-label',
13476 html : this.fieldLabel
13487 if(this.labelWidth > 12){
13488 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13491 if(this.labelWidth < 13 && this.labelmd == 0){
13492 this.labelmd = this.labelWidth;
13495 if(this.labellg > 0){
13496 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13497 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13500 if(this.labelmd > 0){
13501 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13502 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13505 if(this.labelsm > 0){
13506 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13507 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13510 if(this.labelxs > 0){
13511 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13512 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13515 } else if ( this.fieldLabel.length) {
13520 //cls : 'input-group-addon',
13521 html : this.fieldLabel
13539 if (this.disabled) {
13540 input.disabled=true;
13547 * return the real textarea element.
13549 inputEl: function ()
13551 return this.el.select('textarea.form-control',true).first();
13555 * Clear any invalid styles/messages for this field
13557 clearInvalid : function()
13560 if(!this.el || this.preventMark){ // not rendered
13564 var label = this.el.select('label', true).first();
13565 var icon = this.el.select('i.fa-star', true).first();
13570 this.el.removeClass( this.validClass);
13571 this.inputEl().removeClass('is-invalid');
13573 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13575 var feedback = this.el.select('.form-control-feedback', true).first();
13578 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13583 this.fireEvent('valid', this);
13587 * Mark this field as valid
13589 markValid : function()
13591 if(!this.el || this.preventMark){ // not rendered
13595 this.el.removeClass([this.invalidClass, this.validClass]);
13596 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13598 var feedback = this.el.select('.form-control-feedback', true).first();
13601 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13604 if(this.disabled || this.allowBlank){
13608 var label = this.el.select('label', true).first();
13609 var icon = this.el.select('i.fa-star', true).first();
13614 if (Roo.bootstrap.version == 3) {
13615 this.el.addClass(this.validClass);
13617 this.inputEl().addClass('is-valid');
13621 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13623 var feedback = this.el.select('.form-control-feedback', true).first();
13626 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13627 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13632 this.fireEvent('valid', this);
13636 * Mark this field as invalid
13637 * @param {String} msg The validation message
13639 markInvalid : function(msg)
13641 if(!this.el || this.preventMark){ // not rendered
13645 this.el.removeClass([this.invalidClass, this.validClass]);
13646 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13648 var feedback = this.el.select('.form-control-feedback', true).first();
13651 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13654 if(this.disabled || this.allowBlank){
13658 var label = this.el.select('label', true).first();
13659 var icon = this.el.select('i.fa-star', true).first();
13661 if(!this.getValue().length && label && !icon){
13662 this.el.createChild({
13664 cls : 'text-danger fa fa-lg fa-star',
13665 tooltip : 'This field is required',
13666 style : 'margin-right:5px;'
13670 if (Roo.bootstrap.version == 3) {
13671 this.el.addClass(this.invalidClass);
13673 this.inputEl().addClass('is-invalid');
13676 // fixme ... this may be depricated need to test..
13677 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13679 var feedback = this.el.select('.form-control-feedback', true).first();
13682 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13684 if(this.getValue().length || this.forceFeedback){
13685 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13692 this.fireEvent('invalid', this, msg);
13700 * trigger field - base class for combo..
13705 * @class Roo.bootstrap.TriggerField
13706 * @extends Roo.bootstrap.Input
13707 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13708 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13709 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13710 * for which you can provide a custom implementation. For example:
13712 var trigger = new Roo.bootstrap.TriggerField();
13713 trigger.onTriggerClick = myTriggerFn;
13714 trigger.applyTo('my-field');
13717 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13718 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13719 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13720 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13721 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13724 * Create a new TriggerField.
13725 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13726 * to the base TextField)
13728 Roo.bootstrap.TriggerField = function(config){
13729 this.mimicing = false;
13730 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13733 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13735 * @cfg {String} triggerClass A CSS class to apply to the trigger
13738 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13743 * @cfg {Boolean} removable (true|false) special filter default false
13747 /** @cfg {Boolean} grow @hide */
13748 /** @cfg {Number} growMin @hide */
13749 /** @cfg {Number} growMax @hide */
13755 autoSize: Roo.emptyFn,
13759 deferHeight : true,
13762 actionMode : 'wrap',
13767 getAutoCreate : function(){
13769 var align = this.labelAlign || this.parentLabelAlign();
13774 cls: 'form-group' //input-group
13781 type : this.inputType,
13782 cls : 'form-control',
13783 autocomplete: 'new-password',
13784 placeholder : this.placeholder || ''
13788 input.name = this.name;
13791 input.cls += ' input-' + this.size;
13794 if (this.disabled) {
13795 input.disabled=true;
13798 var inputblock = input;
13800 if(this.hasFeedback && !this.allowBlank){
13804 cls: 'glyphicon form-control-feedback'
13807 if(this.removable && !this.editable ){
13809 cls : 'has-feedback',
13815 cls : 'roo-combo-removable-btn close'
13822 cls : 'has-feedback',
13831 if(this.removable && !this.editable ){
13833 cls : 'roo-removable',
13839 cls : 'roo-combo-removable-btn close'
13846 if (this.before || this.after) {
13849 cls : 'input-group',
13853 inputblock.cn.push({
13855 cls : 'input-group-addon input-group-prepend input-group-text',
13860 inputblock.cn.push(input);
13862 if(this.hasFeedback && !this.allowBlank){
13863 inputblock.cls += ' has-feedback';
13864 inputblock.cn.push(feedback);
13868 inputblock.cn.push({
13870 cls : 'input-group-addon input-group-append input-group-text',
13879 var ibwrap = inputblock;
13884 cls: 'roo-select2-choices',
13888 cls: 'roo-select2-search-field',
13900 cls: 'roo-select2-container input-group',
13905 cls: 'form-hidden-field'
13911 if(!this.multiple && this.showToggleBtn){
13917 if (this.caret != false) {
13920 cls: 'fa fa-' + this.caret
13927 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13929 Roo.bootstrap.version == 3 ? caret : '',
13932 cls: 'combobox-clear',
13946 combobox.cls += ' roo-select2-container-multi';
13950 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13951 tooltip : 'This field is required'
13953 if (Roo.bootstrap.version == 4) {
13956 style : 'display:none'
13961 if (align ==='left' && this.fieldLabel.length) {
13963 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13970 cls : 'control-label',
13971 html : this.fieldLabel
13983 var labelCfg = cfg.cn[1];
13984 var contentCfg = cfg.cn[2];
13986 if(this.indicatorpos == 'right'){
13991 cls : 'control-label',
13995 html : this.fieldLabel
14009 labelCfg = cfg.cn[0];
14010 contentCfg = cfg.cn[1];
14013 if(this.labelWidth > 12){
14014 labelCfg.style = "width: " + this.labelWidth + 'px';
14017 if(this.labelWidth < 13 && this.labelmd == 0){
14018 this.labelmd = this.labelWidth;
14021 if(this.labellg > 0){
14022 labelCfg.cls += ' col-lg-' + this.labellg;
14023 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14026 if(this.labelmd > 0){
14027 labelCfg.cls += ' col-md-' + this.labelmd;
14028 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14031 if(this.labelsm > 0){
14032 labelCfg.cls += ' col-sm-' + this.labelsm;
14033 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14036 if(this.labelxs > 0){
14037 labelCfg.cls += ' col-xs-' + this.labelxs;
14038 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14041 } else if ( this.fieldLabel.length) {
14042 // Roo.log(" label");
14047 //cls : 'input-group-addon',
14048 html : this.fieldLabel
14056 if(this.indicatorpos == 'right'){
14064 html : this.fieldLabel
14078 // Roo.log(" no label && no align");
14085 ['xs','sm','md','lg'].map(function(size){
14086 if (settings[size]) {
14087 cfg.cls += ' col-' + size + '-' + settings[size];
14098 onResize : function(w, h){
14099 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
14100 // if(typeof w == 'number'){
14101 // var x = w - this.trigger.getWidth();
14102 // this.inputEl().setWidth(this.adjustWidth('input', x));
14103 // this.trigger.setStyle('left', x+'px');
14108 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14111 getResizeEl : function(){
14112 return this.inputEl();
14116 getPositionEl : function(){
14117 return this.inputEl();
14121 alignErrorIcon : function(){
14122 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14126 initEvents : function(){
14130 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
14131 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14132 if(!this.multiple && this.showToggleBtn){
14133 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14134 if(this.hideTrigger){
14135 this.trigger.setDisplayed(false);
14137 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14141 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14144 if(this.removable && !this.editable && !this.tickable){
14145 var close = this.closeTriggerEl();
14148 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14149 close.on('click', this.removeBtnClick, this, close);
14153 //this.trigger.addClassOnOver('x-form-trigger-over');
14154 //this.trigger.addClassOnClick('x-form-trigger-click');
14157 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14161 closeTriggerEl : function()
14163 var close = this.el.select('.roo-combo-removable-btn', true).first();
14164 return close ? close : false;
14167 removeBtnClick : function(e, h, el)
14169 e.preventDefault();
14171 if(this.fireEvent("remove", this) !== false){
14173 this.fireEvent("afterremove", this)
14177 createList : function()
14179 this.list = Roo.get(document.body).createChild({
14180 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14181 cls: 'typeahead typeahead-long dropdown-menu shadow',
14182 style: 'display:none'
14185 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14190 initTrigger : function(){
14195 onDestroy : function(){
14197 this.trigger.removeAllListeners();
14198 // this.trigger.remove();
14201 // this.wrap.remove();
14203 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
14207 onFocus : function(){
14208 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
14210 if(!this.mimicing){
14211 this.wrap.addClass('x-trigger-wrap-focus');
14212 this.mimicing = true;
14213 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14214 if(this.monitorTab){
14215 this.el.on("keydown", this.checkTab, this);
14222 checkTab : function(e){
14223 if(e.getKey() == e.TAB){
14224 this.triggerBlur();
14229 onBlur : function(){
14234 mimicBlur : function(e, t){
14236 if(!this.wrap.contains(t) && this.validateBlur()){
14237 this.triggerBlur();
14243 triggerBlur : function(){
14244 this.mimicing = false;
14245 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14246 if(this.monitorTab){
14247 this.el.un("keydown", this.checkTab, this);
14249 //this.wrap.removeClass('x-trigger-wrap-focus');
14250 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
14254 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14255 validateBlur : function(e, t){
14260 onDisable : function(){
14261 this.inputEl().dom.disabled = true;
14262 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
14264 // this.wrap.addClass('x-item-disabled');
14269 onEnable : function(){
14270 this.inputEl().dom.disabled = false;
14271 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
14273 // this.el.removeClass('x-item-disabled');
14278 onShow : function(){
14279 var ae = this.getActionEl();
14282 ae.dom.style.display = '';
14283 ae.dom.style.visibility = 'visible';
14289 onHide : function(){
14290 var ae = this.getActionEl();
14291 ae.dom.style.display = 'none';
14295 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14296 * by an implementing function.
14298 * @param {EventObject} e
14300 onTriggerClick : Roo.emptyFn
14308 * @class Roo.bootstrap.CardUploader
14309 * @extends Roo.bootstrap.Button
14310 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14311 * @cfg {Number} errorTimeout default 3000
14312 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14313 * @cfg {Array} html The button text.
14317 * Create a new CardUploader
14318 * @param {Object} config The config object
14321 Roo.bootstrap.CardUploader = function(config){
14325 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
14328 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14336 * When a image is clicked on - and needs to display a slideshow or similar..
14337 * @param {Roo.bootstrap.Card} this
14338 * @param {Object} The image information data
14344 * When a the download link is clicked
14345 * @param {Roo.bootstrap.Card} this
14346 * @param {Object} The image information data contains
14353 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
14356 errorTimeout : 3000,
14360 fileCollection : false,
14363 getAutoCreate : function()
14367 cls :'form-group' ,
14372 //cls : 'input-group-addon',
14373 html : this.fieldLabel
14381 value : this.value,
14382 cls : 'd-none form-control'
14387 multiple : 'multiple',
14389 cls : 'd-none roo-card-upload-selector'
14393 cls : 'roo-card-uploader-button-container w-100 mb-2'
14396 cls : 'card-columns roo-card-uploader-container'
14406 getChildContainer : function() /// what children are added to.
14408 return this.containerEl;
14411 getButtonContainer : function() /// what children are added to.
14413 return this.el.select(".roo-card-uploader-button-container").first();
14416 initEvents : function()
14419 Roo.bootstrap.Input.prototype.initEvents.call(this);
14423 xns: Roo.bootstrap,
14426 container_method : 'getButtonContainer' ,
14427 html : this.html, // fix changable?
14430 'click' : function(btn, e) {
14439 this.urlAPI = (window.createObjectURL && window) ||
14440 (window.URL && URL.revokeObjectURL && URL) ||
14441 (window.webkitURL && webkitURL);
14446 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14448 this.selectorEl.on('change', this.onFileSelected, this);
14451 this.images.forEach(function(img) {
14454 this.images = false;
14456 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14462 onClick : function(e)
14464 e.preventDefault();
14466 this.selectorEl.dom.click();
14470 onFileSelected : function(e)
14472 e.preventDefault();
14474 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14478 Roo.each(this.selectorEl.dom.files, function(file){
14479 this.addFile(file);
14488 addFile : function(file)
14491 if(typeof(file) === 'string'){
14492 throw "Add file by name?"; // should not happen
14496 if(!file || !this.urlAPI){
14506 var url = _this.urlAPI.createObjectURL( file);
14509 id : Roo.bootstrap.CardUploader.ID--,
14510 is_uploaded : false,
14514 mimetype : file.type,
14522 * addCard - add an Attachment to the uploader
14523 * @param data - the data about the image to upload
14527 title : "Title of file",
14528 is_uploaded : false,
14529 src : "http://.....",
14530 srcfile : { the File upload object },
14531 mimetype : file.type,
14534 .. any other data...
14540 addCard : function (data)
14542 // hidden input element?
14543 // if the file is not an image...
14544 //then we need to use something other that and header_image
14549 xns : Roo.bootstrap,
14550 xtype : 'CardFooter',
14553 xns : Roo.bootstrap,
14559 xns : Roo.bootstrap,
14561 html : String.format("<small>{0}</small>", data.title),
14562 cls : 'col-10 text-left',
14567 click : function() {
14569 t.fireEvent( "download", t, data );
14575 xns : Roo.bootstrap,
14577 style: 'max-height: 28px; ',
14583 click : function() {
14584 t.removeCard(data.id)
14596 var cn = this.addxtype(
14599 xns : Roo.bootstrap,
14602 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14603 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14604 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14609 initEvents : function() {
14610 Roo.bootstrap.Card.prototype.initEvents.call(this);
14612 this.imgEl = this.el.select('.card-img-top').first();
14614 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14615 this.imgEl.set({ 'pointer' : 'cursor' });
14618 this.getCardFooter().addClass('p-1');
14625 // dont' really need ot update items.
14626 // this.items.push(cn);
14627 this.fileCollection.add(cn);
14629 if (!data.srcfile) {
14630 this.updateInput();
14635 var reader = new FileReader();
14636 reader.addEventListener("load", function() {
14637 data.srcdata = reader.result;
14640 reader.readAsDataURL(data.srcfile);
14645 removeCard : function(id)
14648 var card = this.fileCollection.get(id);
14649 card.data.is_deleted = 1;
14650 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14651 //this.fileCollection.remove(card);
14652 //this.items = this.items.filter(function(e) { return e != card });
14653 // dont' really need ot update items.
14654 card.el.dom.parentNode.removeChild(card.el.dom);
14655 this.updateInput();
14661 this.fileCollection.each(function(card) {
14662 if (card.el.dom && card.el.dom.parentNode) {
14663 card.el.dom.parentNode.removeChild(card.el.dom);
14666 this.fileCollection.clear();
14667 this.updateInput();
14670 updateInput : function()
14673 this.fileCollection.each(function(e) {
14677 this.inputEl().dom.value = JSON.stringify(data);
14687 Roo.bootstrap.CardUploader.ID = -1;/*
14689 * Ext JS Library 1.1.1
14690 * Copyright(c) 2006-2007, Ext JS, LLC.
14692 * Originally Released Under LGPL - original licence link has changed is not relivant.
14695 * <script type="text/javascript">
14700 * @class Roo.data.SortTypes
14702 * Defines the default sorting (casting?) comparison functions used when sorting data.
14704 Roo.data.SortTypes = {
14706 * Default sort that does nothing
14707 * @param {Mixed} s The value being converted
14708 * @return {Mixed} The comparison value
14710 none : function(s){
14715 * The regular expression used to strip tags
14719 stripTagsRE : /<\/?[^>]+>/gi,
14722 * Strips all HTML tags to sort on text only
14723 * @param {Mixed} s The value being converted
14724 * @return {String} The comparison value
14726 asText : function(s){
14727 return String(s).replace(this.stripTagsRE, "");
14731 * Strips all HTML tags to sort on text only - Case insensitive
14732 * @param {Mixed} s The value being converted
14733 * @return {String} The comparison value
14735 asUCText : function(s){
14736 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14740 * Case insensitive string
14741 * @param {Mixed} s The value being converted
14742 * @return {String} The comparison value
14744 asUCString : function(s) {
14745 return String(s).toUpperCase();
14750 * @param {Mixed} s The value being converted
14751 * @return {Number} The comparison value
14753 asDate : function(s) {
14757 if(s instanceof Date){
14758 return s.getTime();
14760 return Date.parse(String(s));
14765 * @param {Mixed} s The value being converted
14766 * @return {Float} The comparison value
14768 asFloat : function(s) {
14769 var val = parseFloat(String(s).replace(/,/g, ""));
14778 * @param {Mixed} s The value being converted
14779 * @return {Number} The comparison value
14781 asInt : function(s) {
14782 var val = parseInt(String(s).replace(/,/g, ""));
14790 * Ext JS Library 1.1.1
14791 * Copyright(c) 2006-2007, Ext JS, LLC.
14793 * Originally Released Under LGPL - original licence link has changed is not relivant.
14796 * <script type="text/javascript">
14800 * @class Roo.data.Record
14801 * Instances of this class encapsulate both record <em>definition</em> information, and record
14802 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14803 * to access Records cached in an {@link Roo.data.Store} object.<br>
14805 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14806 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14809 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14811 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14812 * {@link #create}. The parameters are the same.
14813 * @param {Array} data An associative Array of data values keyed by the field name.
14814 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14815 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14816 * not specified an integer id is generated.
14818 Roo.data.Record = function(data, id){
14819 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14824 * Generate a constructor for a specific record layout.
14825 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14826 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14827 * Each field definition object may contain the following properties: <ul>
14828 * <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,
14829 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14830 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14831 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14832 * is being used, then this is a string containing the javascript expression to reference the data relative to
14833 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14834 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14835 * this may be omitted.</p></li>
14836 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14837 * <ul><li>auto (Default, implies no conversion)</li>
14842 * <li>date</li></ul></p></li>
14843 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14844 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14845 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14846 * by the Reader into an object that will be stored in the Record. It is passed the
14847 * following parameters:<ul>
14848 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14850 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14852 * <br>usage:<br><pre><code>
14853 var TopicRecord = Roo.data.Record.create(
14854 {name: 'title', mapping: 'topic_title'},
14855 {name: 'author', mapping: 'username'},
14856 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14857 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14858 {name: 'lastPoster', mapping: 'user2'},
14859 {name: 'excerpt', mapping: 'post_text'}
14862 var myNewRecord = new TopicRecord({
14863 title: 'Do my job please',
14866 lastPost: new Date(),
14867 lastPoster: 'Animal',
14868 excerpt: 'No way dude!'
14870 myStore.add(myNewRecord);
14875 Roo.data.Record.create = function(o){
14876 var f = function(){
14877 f.superclass.constructor.apply(this, arguments);
14879 Roo.extend(f, Roo.data.Record);
14880 var p = f.prototype;
14881 p.fields = new Roo.util.MixedCollection(false, function(field){
14884 for(var i = 0, len = o.length; i < len; i++){
14885 p.fields.add(new Roo.data.Field(o[i]));
14887 f.getField = function(name){
14888 return p.fields.get(name);
14893 Roo.data.Record.AUTO_ID = 1000;
14894 Roo.data.Record.EDIT = 'edit';
14895 Roo.data.Record.REJECT = 'reject';
14896 Roo.data.Record.COMMIT = 'commit';
14898 Roo.data.Record.prototype = {
14900 * Readonly flag - true if this record has been modified.
14909 join : function(store){
14910 this.store = store;
14914 * Set the named field to the specified value.
14915 * @param {String} name The name of the field to set.
14916 * @param {Object} value The value to set the field to.
14918 set : function(name, value){
14919 if(this.data[name] == value){
14923 if(!this.modified){
14924 this.modified = {};
14926 if(typeof this.modified[name] == 'undefined'){
14927 this.modified[name] = this.data[name];
14929 this.data[name] = value;
14930 if(!this.editing && this.store){
14931 this.store.afterEdit(this);
14936 * Get the value of the named field.
14937 * @param {String} name The name of the field to get the value of.
14938 * @return {Object} The value of the field.
14940 get : function(name){
14941 return this.data[name];
14945 beginEdit : function(){
14946 this.editing = true;
14947 this.modified = {};
14951 cancelEdit : function(){
14952 this.editing = false;
14953 delete this.modified;
14957 endEdit : function(){
14958 this.editing = false;
14959 if(this.dirty && this.store){
14960 this.store.afterEdit(this);
14965 * Usually called by the {@link Roo.data.Store} which owns the Record.
14966 * Rejects all changes made to the Record since either creation, or the last commit operation.
14967 * Modified fields are reverted to their original values.
14969 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14970 * of reject operations.
14972 reject : function(){
14973 var m = this.modified;
14975 if(typeof m[n] != "function"){
14976 this.data[n] = m[n];
14979 this.dirty = false;
14980 delete this.modified;
14981 this.editing = false;
14983 this.store.afterReject(this);
14988 * Usually called by the {@link Roo.data.Store} which owns the Record.
14989 * Commits all changes made to the Record since either creation, or the last commit operation.
14991 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14992 * of commit operations.
14994 commit : function(){
14995 this.dirty = false;
14996 delete this.modified;
14997 this.editing = false;
14999 this.store.afterCommit(this);
15004 hasError : function(){
15005 return this.error != null;
15009 clearError : function(){
15014 * Creates a copy of this record.
15015 * @param {String} id (optional) A new record id if you don't want to use this record's id
15018 copy : function(newId) {
15019 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15023 * Ext JS Library 1.1.1
15024 * Copyright(c) 2006-2007, Ext JS, LLC.
15026 * Originally Released Under LGPL - original licence link has changed is not relivant.
15029 * <script type="text/javascript">
15035 * @class Roo.data.Store
15036 * @extends Roo.util.Observable
15037 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15038 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15040 * 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
15041 * has no knowledge of the format of the data returned by the Proxy.<br>
15043 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15044 * instances from the data object. These records are cached and made available through accessor functions.
15046 * Creates a new Store.
15047 * @param {Object} config A config object containing the objects needed for the Store to access data,
15048 * and read the data into Records.
15050 Roo.data.Store = function(config){
15051 this.data = new Roo.util.MixedCollection(false);
15052 this.data.getKey = function(o){
15055 this.baseParams = {};
15057 this.paramNames = {
15062 "multisort" : "_multisort"
15065 if(config && config.data){
15066 this.inlineData = config.data;
15067 delete config.data;
15070 Roo.apply(this, config);
15072 if(this.reader){ // reader passed
15073 this.reader = Roo.factory(this.reader, Roo.data);
15074 this.reader.xmodule = this.xmodule || false;
15075 if(!this.recordType){
15076 this.recordType = this.reader.recordType;
15078 if(this.reader.onMetaChange){
15079 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15083 if(this.recordType){
15084 this.fields = this.recordType.prototype.fields;
15086 this.modified = [];
15090 * @event datachanged
15091 * Fires when the data cache has changed, and a widget which is using this Store
15092 * as a Record cache should refresh its view.
15093 * @param {Store} this
15095 datachanged : true,
15097 * @event metachange
15098 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15099 * @param {Store} this
15100 * @param {Object} meta The JSON metadata
15105 * Fires when Records have been added to the Store
15106 * @param {Store} this
15107 * @param {Roo.data.Record[]} records The array of Records added
15108 * @param {Number} index The index at which the record(s) were added
15113 * Fires when a Record has been removed from the Store
15114 * @param {Store} this
15115 * @param {Roo.data.Record} record The Record that was removed
15116 * @param {Number} index The index at which the record was removed
15121 * Fires when a Record has been updated
15122 * @param {Store} this
15123 * @param {Roo.data.Record} record The Record that was updated
15124 * @param {String} operation The update operation being performed. Value may be one of:
15126 Roo.data.Record.EDIT
15127 Roo.data.Record.REJECT
15128 Roo.data.Record.COMMIT
15134 * Fires when the data cache has been cleared.
15135 * @param {Store} this
15139 * @event beforeload
15140 * Fires before a request is made for a new data object. If the beforeload handler returns false
15141 * the load action will be canceled.
15142 * @param {Store} this
15143 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15147 * @event beforeloadadd
15148 * Fires after a new set of Records has been loaded.
15149 * @param {Store} this
15150 * @param {Roo.data.Record[]} records The Records that were loaded
15151 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15153 beforeloadadd : true,
15156 * Fires after a new set of Records has been loaded, before they are added to the store.
15157 * @param {Store} this
15158 * @param {Roo.data.Record[]} records The Records that were loaded
15159 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15160 * @params {Object} return from reader
15164 * @event loadexception
15165 * Fires if an exception occurs in the Proxy during loading.
15166 * Called with the signature of the Proxy's "loadexception" event.
15167 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15170 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15171 * @param {Object} load options
15172 * @param {Object} jsonData from your request (normally this contains the Exception)
15174 loadexception : true
15178 this.proxy = Roo.factory(this.proxy, Roo.data);
15179 this.proxy.xmodule = this.xmodule || false;
15180 this.relayEvents(this.proxy, ["loadexception"]);
15182 this.sortToggle = {};
15183 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15185 Roo.data.Store.superclass.constructor.call(this);
15187 if(this.inlineData){
15188 this.loadData(this.inlineData);
15189 delete this.inlineData;
15193 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15195 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15196 * without a remote query - used by combo/forms at present.
15200 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15203 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15206 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15207 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15210 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15211 * on any HTTP request
15214 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15217 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15221 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15222 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15224 remoteSort : false,
15227 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15228 * loaded or when a record is removed. (defaults to false).
15230 pruneModifiedRecords : false,
15233 lastOptions : null,
15236 * Add Records to the Store and fires the add event.
15237 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15239 add : function(records){
15240 records = [].concat(records);
15241 for(var i = 0, len = records.length; i < len; i++){
15242 records[i].join(this);
15244 var index = this.data.length;
15245 this.data.addAll(records);
15246 this.fireEvent("add", this, records, index);
15250 * Remove a Record from the Store and fires the remove event.
15251 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15253 remove : function(record){
15254 var index = this.data.indexOf(record);
15255 this.data.removeAt(index);
15257 if(this.pruneModifiedRecords){
15258 this.modified.remove(record);
15260 this.fireEvent("remove", this, record, index);
15264 * Remove all Records from the Store and fires the clear event.
15266 removeAll : function(){
15268 if(this.pruneModifiedRecords){
15269 this.modified = [];
15271 this.fireEvent("clear", this);
15275 * Inserts Records to the Store at the given index and fires the add event.
15276 * @param {Number} index The start index at which to insert the passed Records.
15277 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15279 insert : function(index, records){
15280 records = [].concat(records);
15281 for(var i = 0, len = records.length; i < len; i++){
15282 this.data.insert(index, records[i]);
15283 records[i].join(this);
15285 this.fireEvent("add", this, records, index);
15289 * Get the index within the cache of the passed Record.
15290 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15291 * @return {Number} The index of the passed Record. Returns -1 if not found.
15293 indexOf : function(record){
15294 return this.data.indexOf(record);
15298 * Get the index within the cache of the Record with the passed id.
15299 * @param {String} id The id of the Record to find.
15300 * @return {Number} The index of the Record. Returns -1 if not found.
15302 indexOfId : function(id){
15303 return this.data.indexOfKey(id);
15307 * Get the Record with the specified id.
15308 * @param {String} id The id of the Record to find.
15309 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15311 getById : function(id){
15312 return this.data.key(id);
15316 * Get the Record at the specified index.
15317 * @param {Number} index The index of the Record to find.
15318 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15320 getAt : function(index){
15321 return this.data.itemAt(index);
15325 * Returns a range of Records between specified indices.
15326 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15327 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15328 * @return {Roo.data.Record[]} An array of Records
15330 getRange : function(start, end){
15331 return this.data.getRange(start, end);
15335 storeOptions : function(o){
15336 o = Roo.apply({}, o);
15339 this.lastOptions = o;
15343 * Loads the Record cache from the configured Proxy using the configured Reader.
15345 * If using remote paging, then the first load call must specify the <em>start</em>
15346 * and <em>limit</em> properties in the options.params property to establish the initial
15347 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15349 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15350 * and this call will return before the new data has been loaded. Perform any post-processing
15351 * in a callback function, or in a "load" event handler.</strong>
15353 * @param {Object} options An object containing properties which control loading options:<ul>
15354 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15355 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15356 * passed the following arguments:<ul>
15357 * <li>r : Roo.data.Record[]</li>
15358 * <li>options: Options object from the load call</li>
15359 * <li>success: Boolean success indicator</li></ul></li>
15360 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15361 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15364 load : function(options){
15365 options = options || {};
15366 if(this.fireEvent("beforeload", this, options) !== false){
15367 this.storeOptions(options);
15368 var p = Roo.apply(options.params || {}, this.baseParams);
15369 // if meta was not loaded from remote source.. try requesting it.
15370 if (!this.reader.metaFromRemote) {
15371 p._requestMeta = 1;
15373 if(this.sortInfo && this.remoteSort){
15374 var pn = this.paramNames;
15375 p[pn["sort"]] = this.sortInfo.field;
15376 p[pn["dir"]] = this.sortInfo.direction;
15378 if (this.multiSort) {
15379 var pn = this.paramNames;
15380 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15383 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15388 * Reloads the Record cache from the configured Proxy using the configured Reader and
15389 * the options from the last load operation performed.
15390 * @param {Object} options (optional) An object containing properties which may override the options
15391 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15392 * the most recently used options are reused).
15394 reload : function(options){
15395 this.load(Roo.applyIf(options||{}, this.lastOptions));
15399 // Called as a callback by the Reader during a load operation.
15400 loadRecords : function(o, options, success){
15401 if(!o || success === false){
15402 if(success !== false){
15403 this.fireEvent("load", this, [], options, o);
15405 if(options.callback){
15406 options.callback.call(options.scope || this, [], options, false);
15410 // if data returned failure - throw an exception.
15411 if (o.success === false) {
15412 // show a message if no listener is registered.
15413 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15414 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15416 // loadmask wil be hooked into this..
15417 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15420 var r = o.records, t = o.totalRecords || r.length;
15422 this.fireEvent("beforeloadadd", this, r, options, o);
15424 if(!options || options.add !== true){
15425 if(this.pruneModifiedRecords){
15426 this.modified = [];
15428 for(var i = 0, len = r.length; i < len; i++){
15432 this.data = this.snapshot;
15433 delete this.snapshot;
15436 this.data.addAll(r);
15437 this.totalLength = t;
15439 this.fireEvent("datachanged", this);
15441 this.totalLength = Math.max(t, this.data.length+r.length);
15445 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15447 var e = new Roo.data.Record({});
15449 e.set(this.parent.displayField, this.parent.emptyTitle);
15450 e.set(this.parent.valueField, '');
15455 this.fireEvent("load", this, r, options, o);
15456 if(options.callback){
15457 options.callback.call(options.scope || this, r, options, true);
15463 * Loads data from a passed data block. A Reader which understands the format of the data
15464 * must have been configured in the constructor.
15465 * @param {Object} data The data block from which to read the Records. The format of the data expected
15466 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15467 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15469 loadData : function(o, append){
15470 var r = this.reader.readRecords(o);
15471 this.loadRecords(r, {add: append}, true);
15475 * using 'cn' the nested child reader read the child array into it's child stores.
15476 * @param {Object} rec The record with a 'children array
15478 loadDataFromChildren : function(rec)
15480 this.loadData(this.reader.toLoadData(rec));
15485 * Gets the number of cached records.
15487 * <em>If using paging, this may not be the total size of the dataset. If the data object
15488 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15489 * the data set size</em>
15491 getCount : function(){
15492 return this.data.length || 0;
15496 * Gets the total number of records in the dataset as returned by the server.
15498 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15499 * the dataset size</em>
15501 getTotalCount : function(){
15502 return this.totalLength || 0;
15506 * Returns the sort state of the Store as an object with two properties:
15508 field {String} The name of the field by which the Records are sorted
15509 direction {String} The sort order, "ASC" or "DESC"
15512 getSortState : function(){
15513 return this.sortInfo;
15517 applySort : function(){
15518 if(this.sortInfo && !this.remoteSort){
15519 var s = this.sortInfo, f = s.field;
15520 var st = this.fields.get(f).sortType;
15521 var fn = function(r1, r2){
15522 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15523 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15525 this.data.sort(s.direction, fn);
15526 if(this.snapshot && this.snapshot != this.data){
15527 this.snapshot.sort(s.direction, fn);
15533 * Sets the default sort column and order to be used by the next load operation.
15534 * @param {String} fieldName The name of the field to sort by.
15535 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15537 setDefaultSort : function(field, dir){
15538 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15542 * Sort the Records.
15543 * If remote sorting is used, the sort is performed on the server, and the cache is
15544 * reloaded. If local sorting is used, the cache is sorted internally.
15545 * @param {String} fieldName The name of the field to sort by.
15546 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15548 sort : function(fieldName, dir){
15549 var f = this.fields.get(fieldName);
15551 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15553 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15554 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15559 this.sortToggle[f.name] = dir;
15560 this.sortInfo = {field: f.name, direction: dir};
15561 if(!this.remoteSort){
15563 this.fireEvent("datachanged", this);
15565 this.load(this.lastOptions);
15570 * Calls the specified function for each of the Records in the cache.
15571 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15572 * Returning <em>false</em> aborts and exits the iteration.
15573 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15575 each : function(fn, scope){
15576 this.data.each(fn, scope);
15580 * Gets all records modified since the last commit. Modified records are persisted across load operations
15581 * (e.g., during paging).
15582 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15584 getModifiedRecords : function(){
15585 return this.modified;
15589 createFilterFn : function(property, value, anyMatch){
15590 if(!value.exec){ // not a regex
15591 value = String(value);
15592 if(value.length == 0){
15595 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15597 return function(r){
15598 return value.test(r.data[property]);
15603 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15604 * @param {String} property A field on your records
15605 * @param {Number} start The record index to start at (defaults to 0)
15606 * @param {Number} end The last record index to include (defaults to length - 1)
15607 * @return {Number} The sum
15609 sum : function(property, start, end){
15610 var rs = this.data.items, v = 0;
15611 start = start || 0;
15612 end = (end || end === 0) ? end : rs.length-1;
15614 for(var i = start; i <= end; i++){
15615 v += (rs[i].data[property] || 0);
15621 * Filter the records by a specified property.
15622 * @param {String} field A field on your records
15623 * @param {String/RegExp} value Either a string that the field
15624 * should start with or a RegExp to test against the field
15625 * @param {Boolean} anyMatch True to match any part not just the beginning
15627 filter : function(property, value, anyMatch){
15628 var fn = this.createFilterFn(property, value, anyMatch);
15629 return fn ? this.filterBy(fn) : this.clearFilter();
15633 * Filter by a function. The specified function will be called with each
15634 * record in this data source. If the function returns true the record is included,
15635 * otherwise it is filtered.
15636 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15637 * @param {Object} scope (optional) The scope of the function (defaults to this)
15639 filterBy : function(fn, scope){
15640 this.snapshot = this.snapshot || this.data;
15641 this.data = this.queryBy(fn, scope||this);
15642 this.fireEvent("datachanged", this);
15646 * Query the records by a specified property.
15647 * @param {String} field A field on your records
15648 * @param {String/RegExp} value Either a string that the field
15649 * should start with or a RegExp to test against the field
15650 * @param {Boolean} anyMatch True to match any part not just the beginning
15651 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15653 query : function(property, value, anyMatch){
15654 var fn = this.createFilterFn(property, value, anyMatch);
15655 return fn ? this.queryBy(fn) : this.data.clone();
15659 * Query by a function. The specified function will be called with each
15660 * record in this data source. If the function returns true the record is included
15662 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15663 * @param {Object} scope (optional) The scope of the function (defaults to this)
15664 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15666 queryBy : function(fn, scope){
15667 var data = this.snapshot || this.data;
15668 return data.filterBy(fn, scope||this);
15672 * Collects unique values for a particular dataIndex from this store.
15673 * @param {String} dataIndex The property to collect
15674 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15675 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15676 * @return {Array} An array of the unique values
15678 collect : function(dataIndex, allowNull, bypassFilter){
15679 var d = (bypassFilter === true && this.snapshot) ?
15680 this.snapshot.items : this.data.items;
15681 var v, sv, r = [], l = {};
15682 for(var i = 0, len = d.length; i < len; i++){
15683 v = d[i].data[dataIndex];
15685 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15694 * Revert to a view of the Record cache with no filtering applied.
15695 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15697 clearFilter : function(suppressEvent){
15698 if(this.snapshot && this.snapshot != this.data){
15699 this.data = this.snapshot;
15700 delete this.snapshot;
15701 if(suppressEvent !== true){
15702 this.fireEvent("datachanged", this);
15708 afterEdit : function(record){
15709 if(this.modified.indexOf(record) == -1){
15710 this.modified.push(record);
15712 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15716 afterReject : function(record){
15717 this.modified.remove(record);
15718 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15722 afterCommit : function(record){
15723 this.modified.remove(record);
15724 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15728 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15729 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15731 commitChanges : function(){
15732 var m = this.modified.slice(0);
15733 this.modified = [];
15734 for(var i = 0, len = m.length; i < len; i++){
15740 * Cancel outstanding changes on all changed records.
15742 rejectChanges : function(){
15743 var m = this.modified.slice(0);
15744 this.modified = [];
15745 for(var i = 0, len = m.length; i < len; i++){
15750 onMetaChange : function(meta, rtype, o){
15751 this.recordType = rtype;
15752 this.fields = rtype.prototype.fields;
15753 delete this.snapshot;
15754 this.sortInfo = meta.sortInfo || this.sortInfo;
15755 this.modified = [];
15756 this.fireEvent('metachange', this, this.reader.meta);
15759 moveIndex : function(data, type)
15761 var index = this.indexOf(data);
15763 var newIndex = index + type;
15767 this.insert(newIndex, data);
15772 * Ext JS Library 1.1.1
15773 * Copyright(c) 2006-2007, Ext JS, LLC.
15775 * Originally Released Under LGPL - original licence link has changed is not relivant.
15778 * <script type="text/javascript">
15782 * @class Roo.data.SimpleStore
15783 * @extends Roo.data.Store
15784 * Small helper class to make creating Stores from Array data easier.
15785 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15786 * @cfg {Array} fields An array of field definition objects, or field name strings.
15787 * @cfg {Object} an existing reader (eg. copied from another store)
15788 * @cfg {Array} data The multi-dimensional array of data
15789 * @cfg {Roo.data.DataProxy} proxy [not-required]
15790 * @cfg {Roo.data.Reader} reader [not-required]
15792 * @param {Object} config
15794 Roo.data.SimpleStore = function(config)
15796 Roo.data.SimpleStore.superclass.constructor.call(this, {
15798 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15801 Roo.data.Record.create(config.fields)
15803 proxy : new Roo.data.MemoryProxy(config.data)
15807 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15809 * Ext JS Library 1.1.1
15810 * Copyright(c) 2006-2007, Ext JS, LLC.
15812 * Originally Released Under LGPL - original licence link has changed is not relivant.
15815 * <script type="text/javascript">
15820 * @extends Roo.data.Store
15821 * @class Roo.data.JsonStore
15822 * Small helper class to make creating Stores for JSON data easier. <br/>
15824 var store = new Roo.data.JsonStore({
15825 url: 'get-images.php',
15827 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15830 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15831 * JsonReader and HttpProxy (unless inline data is provided).</b>
15832 * @cfg {Array} fields An array of field definition objects, or field name strings.
15834 * @param {Object} config
15836 Roo.data.JsonStore = function(c){
15837 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15838 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15839 reader: new Roo.data.JsonReader(c, c.fields)
15842 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15844 * Ext JS Library 1.1.1
15845 * Copyright(c) 2006-2007, Ext JS, LLC.
15847 * Originally Released Under LGPL - original licence link has changed is not relivant.
15850 * <script type="text/javascript">
15854 Roo.data.Field = function(config){
15855 if(typeof config == "string"){
15856 config = {name: config};
15858 Roo.apply(this, config);
15861 this.type = "auto";
15864 var st = Roo.data.SortTypes;
15865 // named sortTypes are supported, here we look them up
15866 if(typeof this.sortType == "string"){
15867 this.sortType = st[this.sortType];
15870 // set default sortType for strings and dates
15871 if(!this.sortType){
15874 this.sortType = st.asUCString;
15877 this.sortType = st.asDate;
15880 this.sortType = st.none;
15885 var stripRe = /[\$,%]/g;
15887 // prebuilt conversion function for this field, instead of
15888 // switching every time we're reading a value
15890 var cv, dateFormat = this.dateFormat;
15895 cv = function(v){ return v; };
15898 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15902 return v !== undefined && v !== null && v !== '' ?
15903 parseInt(String(v).replace(stripRe, ""), 10) : '';
15908 return v !== undefined && v !== null && v !== '' ?
15909 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15914 cv = function(v){ return v === true || v === "true" || v == 1; };
15921 if(v instanceof Date){
15925 if(dateFormat == "timestamp"){
15926 return new Date(v*1000);
15928 return Date.parseDate(v, dateFormat);
15930 var parsed = Date.parse(v);
15931 return parsed ? new Date(parsed) : null;
15940 Roo.data.Field.prototype = {
15948 * Ext JS Library 1.1.1
15949 * Copyright(c) 2006-2007, Ext JS, LLC.
15951 * Originally Released Under LGPL - original licence link has changed is not relivant.
15954 * <script type="text/javascript">
15957 // Base class for reading structured data from a data source. This class is intended to be
15958 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15961 * @class Roo.data.DataReader
15963 * Base class for reading structured data from a data source. This class is intended to be
15964 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15967 Roo.data.DataReader = function(meta, recordType){
15971 this.recordType = recordType instanceof Array ?
15972 Roo.data.Record.create(recordType) : recordType;
15975 Roo.data.DataReader.prototype = {
15978 readerType : 'Data',
15980 * Create an empty record
15981 * @param {Object} data (optional) - overlay some values
15982 * @return {Roo.data.Record} record created.
15984 newRow : function(d) {
15986 this.recordType.prototype.fields.each(function(c) {
15988 case 'int' : da[c.name] = 0; break;
15989 case 'date' : da[c.name] = new Date(); break;
15990 case 'float' : da[c.name] = 0.0; break;
15991 case 'boolean' : da[c.name] = false; break;
15992 default : da[c.name] = ""; break;
15996 return new this.recordType(Roo.apply(da, d));
16002 * Ext JS Library 1.1.1
16003 * Copyright(c) 2006-2007, Ext JS, LLC.
16005 * Originally Released Under LGPL - original licence link has changed is not relivant.
16008 * <script type="text/javascript">
16012 * @class Roo.data.DataProxy
16013 * @extends Roo.data.Observable
16015 * This class is an abstract base class for implementations which provide retrieval of
16016 * unformatted data objects.<br>
16018 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16019 * (of the appropriate type which knows how to parse the data object) to provide a block of
16020 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16022 * Custom implementations must implement the load method as described in
16023 * {@link Roo.data.HttpProxy#load}.
16025 Roo.data.DataProxy = function(){
16028 * @event beforeload
16029 * Fires before a network request is made to retrieve a data object.
16030 * @param {Object} This DataProxy object.
16031 * @param {Object} params The params parameter to the load function.
16036 * Fires before the load method's callback is called.
16037 * @param {Object} This DataProxy object.
16038 * @param {Object} o The data object.
16039 * @param {Object} arg The callback argument object passed to the load function.
16043 * @event loadexception
16044 * Fires if an Exception occurs during data retrieval.
16045 * @param {Object} This DataProxy object.
16046 * @param {Object} o The data object.
16047 * @param {Object} arg The callback argument object passed to the load function.
16048 * @param {Object} e The Exception.
16050 loadexception : true
16052 Roo.data.DataProxy.superclass.constructor.call(this);
16055 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16058 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16062 * Ext JS Library 1.1.1
16063 * Copyright(c) 2006-2007, Ext JS, LLC.
16065 * Originally Released Under LGPL - original licence link has changed is not relivant.
16068 * <script type="text/javascript">
16071 * @class Roo.data.MemoryProxy
16072 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16073 * to the Reader when its load method is called.
16075 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16077 Roo.data.MemoryProxy = function(data){
16081 Roo.data.MemoryProxy.superclass.constructor.call(this);
16085 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16088 * Load data from the requested source (in this case an in-memory
16089 * data object passed to the constructor), read the data object into
16090 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16091 * process that block using the passed callback.
16092 * @param {Object} params This parameter is not used by the MemoryProxy class.
16093 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16094 * object into a block of Roo.data.Records.
16095 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16096 * The function must be passed <ul>
16097 * <li>The Record block object</li>
16098 * <li>The "arg" argument from the load function</li>
16099 * <li>A boolean success indicator</li>
16101 * @param {Object} scope The scope in which to call the callback
16102 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16104 load : function(params, reader, callback, scope, arg){
16105 params = params || {};
16108 result = reader.readRecords(params.data ? params.data :this.data);
16110 this.fireEvent("loadexception", this, arg, null, e);
16111 callback.call(scope, null, arg, false);
16114 callback.call(scope, result, arg, true);
16118 update : function(params, records){
16123 * Ext JS Library 1.1.1
16124 * Copyright(c) 2006-2007, Ext JS, LLC.
16126 * Originally Released Under LGPL - original licence link has changed is not relivant.
16129 * <script type="text/javascript">
16132 * @class Roo.data.HttpProxy
16133 * @extends Roo.data.DataProxy
16134 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16135 * configured to reference a certain URL.<br><br>
16137 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16138 * from which the running page was served.<br><br>
16140 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16142 * Be aware that to enable the browser to parse an XML document, the server must set
16143 * the Content-Type header in the HTTP response to "text/xml".
16145 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16146 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16147 * will be used to make the request.
16149 Roo.data.HttpProxy = function(conn){
16150 Roo.data.HttpProxy.superclass.constructor.call(this);
16151 // is conn a conn config or a real conn?
16153 this.useAjax = !conn || !conn.events;
16157 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16158 // thse are take from connection...
16161 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16164 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16165 * extra parameters to each request made by this object. (defaults to undefined)
16168 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16169 * to each request made by this object. (defaults to undefined)
16172 * @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)
16175 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16178 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16184 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16188 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16189 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16190 * a finer-grained basis than the DataProxy events.
16192 getConnection : function(){
16193 return this.useAjax ? Roo.Ajax : this.conn;
16197 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16198 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16199 * process that block using the passed callback.
16200 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16201 * for the request to the remote server.
16202 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16203 * object into a block of Roo.data.Records.
16204 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16205 * The function must be passed <ul>
16206 * <li>The Record block object</li>
16207 * <li>The "arg" argument from the load function</li>
16208 * <li>A boolean success indicator</li>
16210 * @param {Object} scope The scope in which to call the callback
16211 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16213 load : function(params, reader, callback, scope, arg){
16214 if(this.fireEvent("beforeload", this, params) !== false){
16216 params : params || {},
16218 callback : callback,
16223 callback : this.loadResponse,
16227 Roo.applyIf(o, this.conn);
16228 if(this.activeRequest){
16229 Roo.Ajax.abort(this.activeRequest);
16231 this.activeRequest = Roo.Ajax.request(o);
16233 this.conn.request(o);
16236 callback.call(scope||this, null, arg, false);
16241 loadResponse : function(o, success, response){
16242 delete this.activeRequest;
16244 this.fireEvent("loadexception", this, o, response);
16245 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16250 result = o.reader.read(response);
16252 this.fireEvent("loadexception", this, o, response, e);
16253 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16257 this.fireEvent("load", this, o, o.request.arg);
16258 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16262 update : function(dataSet){
16267 updateResponse : function(dataSet){
16272 * Ext JS Library 1.1.1
16273 * Copyright(c) 2006-2007, Ext JS, LLC.
16275 * Originally Released Under LGPL - original licence link has changed is not relivant.
16278 * <script type="text/javascript">
16282 * @class Roo.data.ScriptTagProxy
16283 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16284 * other than the originating domain of the running page.<br><br>
16286 * <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
16287 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16289 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16290 * source code that is used as the source inside a <script> tag.<br><br>
16292 * In order for the browser to process the returned data, the server must wrap the data object
16293 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16294 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16295 * depending on whether the callback name was passed:
16298 boolean scriptTag = false;
16299 String cb = request.getParameter("callback");
16302 response.setContentType("text/javascript");
16304 response.setContentType("application/x-json");
16306 Writer out = response.getWriter();
16308 out.write(cb + "(");
16310 out.print(dataBlock.toJsonString());
16317 * @param {Object} config A configuration object.
16319 Roo.data.ScriptTagProxy = function(config){
16320 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16321 Roo.apply(this, config);
16322 this.head = document.getElementsByTagName("head")[0];
16325 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16327 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16329 * @cfg {String} url The URL from which to request the data object.
16332 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16336 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16337 * the server the name of the callback function set up by the load call to process the returned data object.
16338 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16339 * javascript output which calls this named function passing the data object as its only parameter.
16341 callbackParam : "callback",
16343 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16344 * name to the request.
16349 * Load data from the configured URL, read the data object into
16350 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16351 * process that block using the passed callback.
16352 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16353 * for the request to the remote server.
16354 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16355 * object into a block of Roo.data.Records.
16356 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16357 * The function must be passed <ul>
16358 * <li>The Record block object</li>
16359 * <li>The "arg" argument from the load function</li>
16360 * <li>A boolean success indicator</li>
16362 * @param {Object} scope The scope in which to call the callback
16363 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16365 load : function(params, reader, callback, scope, arg){
16366 if(this.fireEvent("beforeload", this, params) !== false){
16368 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16370 var url = this.url;
16371 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16373 url += "&_dc=" + (new Date().getTime());
16375 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16378 cb : "stcCallback"+transId,
16379 scriptId : "stcScript"+transId,
16383 callback : callback,
16389 window[trans.cb] = function(o){
16390 conn.handleResponse(o, trans);
16393 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16395 if(this.autoAbort !== false){
16399 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16401 var script = document.createElement("script");
16402 script.setAttribute("src", url);
16403 script.setAttribute("type", "text/javascript");
16404 script.setAttribute("id", trans.scriptId);
16405 this.head.appendChild(script);
16407 this.trans = trans;
16409 callback.call(scope||this, null, arg, false);
16414 isLoading : function(){
16415 return this.trans ? true : false;
16419 * Abort the current server request.
16421 abort : function(){
16422 if(this.isLoading()){
16423 this.destroyTrans(this.trans);
16428 destroyTrans : function(trans, isLoaded){
16429 this.head.removeChild(document.getElementById(trans.scriptId));
16430 clearTimeout(trans.timeoutId);
16432 window[trans.cb] = undefined;
16434 delete window[trans.cb];
16437 // if hasn't been loaded, wait for load to remove it to prevent script error
16438 window[trans.cb] = function(){
16439 window[trans.cb] = undefined;
16441 delete window[trans.cb];
16448 handleResponse : function(o, trans){
16449 this.trans = false;
16450 this.destroyTrans(trans, true);
16453 result = trans.reader.readRecords(o);
16455 this.fireEvent("loadexception", this, o, trans.arg, e);
16456 trans.callback.call(trans.scope||window, null, trans.arg, false);
16459 this.fireEvent("load", this, o, trans.arg);
16460 trans.callback.call(trans.scope||window, result, trans.arg, true);
16464 handleFailure : function(trans){
16465 this.trans = false;
16466 this.destroyTrans(trans, false);
16467 this.fireEvent("loadexception", this, null, trans.arg);
16468 trans.callback.call(trans.scope||window, null, trans.arg, false);
16472 * Ext JS Library 1.1.1
16473 * Copyright(c) 2006-2007, Ext JS, LLC.
16475 * Originally Released Under LGPL - original licence link has changed is not relivant.
16478 * <script type="text/javascript">
16482 * @class Roo.data.JsonReader
16483 * @extends Roo.data.DataReader
16484 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16485 * based on mappings in a provided Roo.data.Record constructor.
16487 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16488 * in the reply previously.
16493 var RecordDef = Roo.data.Record.create([
16494 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16495 {name: 'occupation'} // This field will use "occupation" as the mapping.
16497 var myReader = new Roo.data.JsonReader({
16498 totalProperty: "results", // The property which contains the total dataset size (optional)
16499 root: "rows", // The property which contains an Array of row objects
16500 id: "id" // The property within each row object that provides an ID for the record (optional)
16504 * This would consume a JSON file like this:
16506 { 'results': 2, 'rows': [
16507 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16508 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16511 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16512 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16513 * paged from the remote server.
16514 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16515 * @cfg {String} root name of the property which contains the Array of row objects.
16516 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16517 * @cfg {Array} fields Array of field definition objects
16519 * Create a new JsonReader
16520 * @param {Object} meta Metadata configuration options
16521 * @param {Object} recordType Either an Array of field definition objects,
16522 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16524 Roo.data.JsonReader = function(meta, recordType){
16527 // set some defaults:
16528 Roo.applyIf(meta, {
16529 totalProperty: 'total',
16530 successProperty : 'success',
16535 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16537 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16539 readerType : 'Json',
16542 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16543 * Used by Store query builder to append _requestMeta to params.
16546 metaFromRemote : false,
16548 * This method is only used by a DataProxy which has retrieved data from a remote server.
16549 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16550 * @return {Object} data A data block which is used by an Roo.data.Store object as
16551 * a cache of Roo.data.Records.
16553 read : function(response){
16554 var json = response.responseText;
16556 var o = /* eval:var:o */ eval("("+json+")");
16558 throw {message: "JsonReader.read: Json object not found"};
16564 this.metaFromRemote = true;
16565 this.meta = o.metaData;
16566 this.recordType = Roo.data.Record.create(o.metaData.fields);
16567 this.onMetaChange(this.meta, this.recordType, o);
16569 return this.readRecords(o);
16572 // private function a store will implement
16573 onMetaChange : function(meta, recordType, o){
16580 simpleAccess: function(obj, subsc) {
16587 getJsonAccessor: function(){
16589 return function(expr) {
16591 return(re.test(expr))
16592 ? new Function("obj", "return obj." + expr)
16597 return Roo.emptyFn;
16602 * Create a data block containing Roo.data.Records from an XML document.
16603 * @param {Object} o An object which contains an Array of row objects in the property specified
16604 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16605 * which contains the total size of the dataset.
16606 * @return {Object} data A data block which is used by an Roo.data.Store object as
16607 * a cache of Roo.data.Records.
16609 readRecords : function(o){
16611 * After any data loads, the raw JSON data is available for further custom processing.
16615 var s = this.meta, Record = this.recordType,
16616 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16618 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16620 if(s.totalProperty) {
16621 this.getTotal = this.getJsonAccessor(s.totalProperty);
16623 if(s.successProperty) {
16624 this.getSuccess = this.getJsonAccessor(s.successProperty);
16626 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16628 var g = this.getJsonAccessor(s.id);
16629 this.getId = function(rec) {
16631 return (r === undefined || r === "") ? null : r;
16634 this.getId = function(){return null;};
16637 for(var jj = 0; jj < fl; jj++){
16639 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16640 this.ef[jj] = this.getJsonAccessor(map);
16644 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16645 if(s.totalProperty){
16646 var vt = parseInt(this.getTotal(o), 10);
16651 if(s.successProperty){
16652 var vs = this.getSuccess(o);
16653 if(vs === false || vs === 'false'){
16658 for(var i = 0; i < c; i++){
16661 var id = this.getId(n);
16662 for(var j = 0; j < fl; j++){
16664 var v = this.ef[j](n);
16666 Roo.log('missing convert for ' + f.name);
16670 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16672 var record = new Record(values, id);
16674 records[i] = record;
16680 totalRecords : totalRecords
16683 // used when loading children.. @see loadDataFromChildren
16684 toLoadData: function(rec)
16686 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16687 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16688 return { data : data, total : data.length };
16693 * Ext JS Library 1.1.1
16694 * Copyright(c) 2006-2007, Ext JS, LLC.
16696 * Originally Released Under LGPL - original licence link has changed is not relivant.
16699 * <script type="text/javascript">
16703 * @class Roo.data.ArrayReader
16704 * @extends Roo.data.DataReader
16705 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16706 * Each element of that Array represents a row of data fields. The
16707 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16708 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16712 var RecordDef = Roo.data.Record.create([
16713 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16714 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16716 var myReader = new Roo.data.ArrayReader({
16717 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16721 * This would consume an Array like this:
16723 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16727 * Create a new JsonReader
16728 * @param {Object} meta Metadata configuration options.
16729 * @param {Object|Array} recordType Either an Array of field definition objects
16731 * @cfg {Array} fields Array of field definition objects
16732 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16733 * as specified to {@link Roo.data.Record#create},
16734 * or an {@link Roo.data.Record} object
16737 * created using {@link Roo.data.Record#create}.
16739 Roo.data.ArrayReader = function(meta, recordType)
16741 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16744 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16747 * Create a data block containing Roo.data.Records from an XML document.
16748 * @param {Object} o An Array of row objects which represents the dataset.
16749 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16750 * a cache of Roo.data.Records.
16752 readRecords : function(o)
16754 var sid = this.meta ? this.meta.id : null;
16755 var recordType = this.recordType, fields = recordType.prototype.fields;
16758 for(var i = 0; i < root.length; i++){
16761 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16762 for(var j = 0, jlen = fields.length; j < jlen; j++){
16763 var f = fields.items[j];
16764 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16765 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16767 values[f.name] = v;
16769 var record = new recordType(values, id);
16771 records[records.length] = record;
16775 totalRecords : records.length
16778 // used when loading children.. @see loadDataFromChildren
16779 toLoadData: function(rec)
16781 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16782 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16793 * @class Roo.bootstrap.ComboBox
16794 * @extends Roo.bootstrap.TriggerField
16795 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16796 * @cfg {Boolean} append (true|false) default false
16797 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16798 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16799 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16800 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16801 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16802 * @cfg {Boolean} animate default true
16803 * @cfg {Boolean} emptyResultText only for touch device
16804 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16805 * @cfg {String} emptyTitle default ''
16806 * @cfg {Number} width fixed with? experimental
16808 * Create a new ComboBox.
16809 * @param {Object} config Configuration options
16811 Roo.bootstrap.ComboBox = function(config){
16812 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16816 * Fires when the dropdown list is expanded
16817 * @param {Roo.bootstrap.ComboBox} combo This combo box
16822 * Fires when the dropdown list is collapsed
16823 * @param {Roo.bootstrap.ComboBox} combo This combo box
16827 * @event beforeselect
16828 * Fires before a list item is selected. Return false to cancel the selection.
16829 * @param {Roo.bootstrap.ComboBox} combo This combo box
16830 * @param {Roo.data.Record} record The data record returned from the underlying store
16831 * @param {Number} index The index of the selected item in the dropdown list
16833 'beforeselect' : true,
16836 * Fires when a list item is selected
16837 * @param {Roo.bootstrap.ComboBox} combo This combo box
16838 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16839 * @param {Number} index The index of the selected item in the dropdown list
16843 * @event beforequery
16844 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16845 * The event object passed has these properties:
16846 * @param {Roo.bootstrap.ComboBox} combo This combo box
16847 * @param {String} query The query
16848 * @param {Boolean} forceAll true to force "all" query
16849 * @param {Boolean} cancel true to cancel the query
16850 * @param {Object} e The query event object
16852 'beforequery': true,
16855 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16856 * @param {Roo.bootstrap.ComboBox} combo This combo box
16861 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16862 * @param {Roo.bootstrap.ComboBox} combo This combo box
16863 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16868 * Fires when the remove value from the combobox array
16869 * @param {Roo.bootstrap.ComboBox} combo This combo box
16873 * @event afterremove
16874 * Fires when the remove value from the combobox array
16875 * @param {Roo.bootstrap.ComboBox} combo This combo box
16877 'afterremove' : true,
16879 * @event specialfilter
16880 * Fires when specialfilter
16881 * @param {Roo.bootstrap.ComboBox} combo This combo box
16883 'specialfilter' : true,
16886 * Fires when tick the element
16887 * @param {Roo.bootstrap.ComboBox} combo This combo box
16891 * @event touchviewdisplay
16892 * Fires when touch view require special display (default is using displayField)
16893 * @param {Roo.bootstrap.ComboBox} combo This combo box
16894 * @param {Object} cfg set html .
16896 'touchviewdisplay' : true
16901 this.tickItems = [];
16903 this.selectedIndex = -1;
16904 if(this.mode == 'local'){
16905 if(config.queryDelay === undefined){
16906 this.queryDelay = 10;
16908 if(config.minChars === undefined){
16914 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16917 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16918 * rendering into an Roo.Editor, defaults to false)
16921 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16922 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16925 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16928 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16929 * the dropdown list (defaults to undefined, with no header element)
16933 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16937 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16939 listWidth: undefined,
16941 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16942 * mode = 'remote' or 'text' if mode = 'local')
16944 displayField: undefined,
16947 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16948 * mode = 'remote' or 'value' if mode = 'local').
16949 * Note: use of a valueField requires the user make a selection
16950 * in order for a value to be mapped.
16952 valueField: undefined,
16954 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16959 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16960 * field's data value (defaults to the underlying DOM element's name)
16962 hiddenName: undefined,
16964 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16968 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16970 selectedClass: 'active',
16973 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16977 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16978 * anchor positions (defaults to 'tl-bl')
16980 listAlign: 'tl-bl?',
16982 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16986 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16987 * query specified by the allQuery config option (defaults to 'query')
16989 triggerAction: 'query',
16991 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16992 * (defaults to 4, does not apply if editable = false)
16996 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16997 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17001 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17002 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17006 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17007 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17011 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17012 * when editable = true (defaults to false)
17014 selectOnFocus:false,
17016 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17018 queryParam: 'query',
17020 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17021 * when mode = 'remote' (defaults to 'Loading...')
17023 loadingText: 'Loading...',
17025 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17029 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17033 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17034 * traditional select (defaults to true)
17038 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17042 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17046 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17047 * listWidth has a higher value)
17051 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17052 * allow the user to set arbitrary text into the field (defaults to false)
17054 forceSelection:false,
17056 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17057 * if typeAhead = true (defaults to 250)
17059 typeAheadDelay : 250,
17061 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17062 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17064 valueNotFoundText : undefined,
17066 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17068 blockFocus : false,
17071 * @cfg {Boolean} disableClear Disable showing of clear button.
17073 disableClear : false,
17075 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17077 alwaysQuery : false,
17080 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17085 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17087 invalidClass : "has-warning",
17090 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17092 validClass : "has-success",
17095 * @cfg {Boolean} specialFilter (true|false) special filter default false
17097 specialFilter : false,
17100 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17102 mobileTouchView : true,
17105 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17107 useNativeIOS : false,
17110 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17112 mobile_restrict_height : false,
17114 ios_options : false,
17126 btnPosition : 'right',
17127 triggerList : true,
17128 showToggleBtn : true,
17130 emptyResultText: 'Empty',
17131 triggerText : 'Select',
17135 // element that contains real text value.. (when hidden is used..)
17137 getAutoCreate : function()
17142 * Render classic select for iso
17145 if(Roo.isIOS && this.useNativeIOS){
17146 cfg = this.getAutoCreateNativeIOS();
17154 if(Roo.isTouch && this.mobileTouchView){
17155 cfg = this.getAutoCreateTouchView();
17162 if(!this.tickable){
17163 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
17168 * ComboBox with tickable selections
17171 var align = this.labelAlign || this.parentLabelAlign();
17174 cls : 'form-group roo-combobox-tickable' //input-group
17177 var btn_text_select = '';
17178 var btn_text_done = '';
17179 var btn_text_cancel = '';
17181 if (this.btn_text_show) {
17182 btn_text_select = 'Select';
17183 btn_text_done = 'Done';
17184 btn_text_cancel = 'Cancel';
17189 cls : 'tickable-buttons',
17194 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17195 //html : this.triggerText
17196 html: btn_text_select
17202 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17204 html: btn_text_done
17210 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17212 html: btn_text_cancel
17218 buttons.cn.unshift({
17220 cls: 'roo-select2-search-field-input'
17226 Roo.each(buttons.cn, function(c){
17228 c.cls += ' btn-' + _this.size;
17231 if (_this.disabled) {
17238 style : 'display: contents',
17243 cls: 'form-hidden-field'
17247 cls: 'roo-select2-choices',
17251 cls: 'roo-select2-search-field',
17262 cls: 'roo-select2-container input-group roo-select2-container-multi',
17268 // cls: 'typeahead typeahead-long dropdown-menu',
17269 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17274 if(this.hasFeedback && !this.allowBlank){
17278 cls: 'glyphicon form-control-feedback'
17281 combobox.cn.push(feedback);
17288 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17289 tooltip : 'This field is required'
17291 if (Roo.bootstrap.version == 4) {
17294 style : 'display:none'
17297 if (align ==='left' && this.fieldLabel.length) {
17299 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17306 cls : 'control-label col-form-label',
17307 html : this.fieldLabel
17319 var labelCfg = cfg.cn[1];
17320 var contentCfg = cfg.cn[2];
17323 if(this.indicatorpos == 'right'){
17329 cls : 'control-label col-form-label',
17333 html : this.fieldLabel
17349 labelCfg = cfg.cn[0];
17350 contentCfg = cfg.cn[1];
17354 if(this.labelWidth > 12){
17355 labelCfg.style = "width: " + this.labelWidth + 'px';
17357 if(this.width * 1 > 0){
17358 contentCfg.style = "width: " + this.width + 'px';
17360 if(this.labelWidth < 13 && this.labelmd == 0){
17361 this.labelmd = this.labelWidth;
17364 if(this.labellg > 0){
17365 labelCfg.cls += ' col-lg-' + this.labellg;
17366 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17369 if(this.labelmd > 0){
17370 labelCfg.cls += ' col-md-' + this.labelmd;
17371 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17374 if(this.labelsm > 0){
17375 labelCfg.cls += ' col-sm-' + this.labelsm;
17376 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17379 if(this.labelxs > 0){
17380 labelCfg.cls += ' col-xs-' + this.labelxs;
17381 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17385 } else if ( this.fieldLabel.length) {
17386 // Roo.log(" label");
17391 //cls : 'input-group-addon',
17392 html : this.fieldLabel
17397 if(this.indicatorpos == 'right'){
17401 //cls : 'input-group-addon',
17402 html : this.fieldLabel
17412 // Roo.log(" no label && no align");
17419 ['xs','sm','md','lg'].map(function(size){
17420 if (settings[size]) {
17421 cfg.cls += ' col-' + size + '-' + settings[size];
17429 _initEventsCalled : false,
17432 initEvents: function()
17434 if (this._initEventsCalled) { // as we call render... prevent looping...
17437 this._initEventsCalled = true;
17440 throw "can not find store for combo";
17443 this.indicator = this.indicatorEl();
17445 this.store = Roo.factory(this.store, Roo.data);
17446 this.store.parent = this;
17448 // if we are building from html. then this element is so complex, that we can not really
17449 // use the rendered HTML.
17450 // so we have to trash and replace the previous code.
17451 if (Roo.XComponent.build_from_html) {
17452 // remove this element....
17453 var e = this.el.dom, k=0;
17454 while (e ) { e = e.previousSibling; ++k;}
17459 this.rendered = false;
17461 this.render(this.parent().getChildContainer(true), k);
17464 if(Roo.isIOS && this.useNativeIOS){
17465 this.initIOSView();
17473 if(Roo.isTouch && this.mobileTouchView){
17474 this.initTouchView();
17479 this.initTickableEvents();
17483 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17485 if(this.hiddenName){
17487 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17489 this.hiddenField.dom.value =
17490 this.hiddenValue !== undefined ? this.hiddenValue :
17491 this.value !== undefined ? this.value : '';
17493 // prevent input submission
17494 this.el.dom.removeAttribute('name');
17495 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17500 // this.el.dom.setAttribute('autocomplete', 'off');
17503 var cls = 'x-combo-list';
17505 //this.list = new Roo.Layer({
17506 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17512 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17513 _this.list.setWidth(lw);
17516 this.list.on('mouseover', this.onViewOver, this);
17517 this.list.on('mousemove', this.onViewMove, this);
17518 this.list.on('scroll', this.onViewScroll, this);
17521 this.list.swallowEvent('mousewheel');
17522 this.assetHeight = 0;
17525 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17526 this.assetHeight += this.header.getHeight();
17529 this.innerList = this.list.createChild({cls:cls+'-inner'});
17530 this.innerList.on('mouseover', this.onViewOver, this);
17531 this.innerList.on('mousemove', this.onViewMove, this);
17532 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17534 if(this.allowBlank && !this.pageSize && !this.disableClear){
17535 this.footer = this.list.createChild({cls:cls+'-ft'});
17536 this.pageTb = new Roo.Toolbar(this.footer);
17540 this.footer = this.list.createChild({cls:cls+'-ft'});
17541 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17542 {pageSize: this.pageSize});
17546 if (this.pageTb && this.allowBlank && !this.disableClear) {
17548 this.pageTb.add(new Roo.Toolbar.Fill(), {
17549 cls: 'x-btn-icon x-btn-clear',
17551 handler: function()
17554 _this.clearValue();
17555 _this.onSelect(false, -1);
17560 this.assetHeight += this.footer.getHeight();
17565 this.tpl = Roo.bootstrap.version == 4 ?
17566 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17567 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17570 this.view = new Roo.View(this.list, this.tpl, {
17571 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17573 //this.view.wrapEl.setDisplayed(false);
17574 this.view.on('click', this.onViewClick, this);
17577 this.store.on('beforeload', this.onBeforeLoad, this);
17578 this.store.on('load', this.onLoad, this);
17579 this.store.on('loadexception', this.onLoadException, this);
17581 if(this.resizable){
17582 this.resizer = new Roo.Resizable(this.list, {
17583 pinned:true, handles:'se'
17585 this.resizer.on('resize', function(r, w, h){
17586 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17587 this.listWidth = w;
17588 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17589 this.restrictHeight();
17591 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17594 if(!this.editable){
17595 this.editable = true;
17596 this.setEditable(false);
17601 if (typeof(this.events.add.listeners) != 'undefined') {
17603 this.addicon = this.wrap.createChild(
17604 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17606 this.addicon.on('click', function(e) {
17607 this.fireEvent('add', this);
17610 if (typeof(this.events.edit.listeners) != 'undefined') {
17612 this.editicon = this.wrap.createChild(
17613 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17614 if (this.addicon) {
17615 this.editicon.setStyle('margin-left', '40px');
17617 this.editicon.on('click', function(e) {
17619 // we fire even if inothing is selected..
17620 this.fireEvent('edit', this, this.lastData );
17626 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17627 "up" : function(e){
17628 this.inKeyMode = true;
17632 "down" : function(e){
17633 if(!this.isExpanded()){
17634 this.onTriggerClick();
17636 this.inKeyMode = true;
17641 "enter" : function(e){
17642 // this.onViewClick();
17646 if(this.fireEvent("specialkey", this, e)){
17647 this.onViewClick(false);
17653 "esc" : function(e){
17657 "tab" : function(e){
17660 if(this.fireEvent("specialkey", this, e)){
17661 this.onViewClick(false);
17669 doRelay : function(foo, bar, hname){
17670 if(hname == 'down' || this.scope.isExpanded()){
17671 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17680 this.queryDelay = Math.max(this.queryDelay || 10,
17681 this.mode == 'local' ? 10 : 250);
17684 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17686 if(this.typeAhead){
17687 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17689 if(this.editable !== false){
17690 this.inputEl().on("keyup", this.onKeyUp, this);
17692 if(this.forceSelection){
17693 this.inputEl().on('blur', this.doForce, this);
17697 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17698 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17702 initTickableEvents: function()
17706 if(this.hiddenName){
17708 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17710 this.hiddenField.dom.value =
17711 this.hiddenValue !== undefined ? this.hiddenValue :
17712 this.value !== undefined ? this.value : '';
17714 // prevent input submission
17715 this.el.dom.removeAttribute('name');
17716 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17721 // this.list = this.el.select('ul.dropdown-menu',true).first();
17723 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17724 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17725 if(this.triggerList){
17726 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17729 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17730 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17732 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17733 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17735 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17736 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17738 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17739 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17740 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17743 this.cancelBtn.hide();
17748 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17749 _this.list.setWidth(lw);
17752 this.list.on('mouseover', this.onViewOver, this);
17753 this.list.on('mousemove', this.onViewMove, this);
17755 this.list.on('scroll', this.onViewScroll, this);
17758 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17759 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17762 this.view = new Roo.View(this.list, this.tpl, {
17767 selectedClass: this.selectedClass
17770 //this.view.wrapEl.setDisplayed(false);
17771 this.view.on('click', this.onViewClick, this);
17775 this.store.on('beforeload', this.onBeforeLoad, this);
17776 this.store.on('load', this.onLoad, this);
17777 this.store.on('loadexception', this.onLoadException, this);
17780 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17781 "up" : function(e){
17782 this.inKeyMode = true;
17786 "down" : function(e){
17787 this.inKeyMode = true;
17791 "enter" : function(e){
17792 if(this.fireEvent("specialkey", this, e)){
17793 this.onViewClick(false);
17799 "esc" : function(e){
17800 this.onTickableFooterButtonClick(e, false, false);
17803 "tab" : function(e){
17804 this.fireEvent("specialkey", this, e);
17806 this.onTickableFooterButtonClick(e, false, false);
17813 doRelay : function(e, fn, key){
17814 if(this.scope.isExpanded()){
17815 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17824 this.queryDelay = Math.max(this.queryDelay || 10,
17825 this.mode == 'local' ? 10 : 250);
17828 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17830 if(this.typeAhead){
17831 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17834 if(this.editable !== false){
17835 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17838 this.indicator = this.indicatorEl();
17840 if(this.indicator){
17841 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17842 this.indicator.hide();
17847 onDestroy : function(){
17849 this.view.setStore(null);
17850 this.view.el.removeAllListeners();
17851 this.view.el.remove();
17852 this.view.purgeListeners();
17855 this.list.dom.innerHTML = '';
17859 this.store.un('beforeload', this.onBeforeLoad, this);
17860 this.store.un('load', this.onLoad, this);
17861 this.store.un('loadexception', this.onLoadException, this);
17863 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17867 fireKey : function(e){
17868 if(e.isNavKeyPress() && !this.list.isVisible()){
17869 this.fireEvent("specialkey", this, e);
17874 onResize: function(w, h)
17878 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17880 // if(typeof w != 'number'){
17881 // // we do not handle it!?!?
17884 // var tw = this.trigger.getWidth();
17885 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17886 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17888 // this.inputEl().setWidth( this.adjustWidth('input', x));
17890 // //this.trigger.setStyle('left', x+'px');
17892 // if(this.list && this.listWidth === undefined){
17893 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17894 // this.list.setWidth(lw);
17895 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17903 * Allow or prevent the user from directly editing the field text. If false is passed,
17904 * the user will only be able to select from the items defined in the dropdown list. This method
17905 * is the runtime equivalent of setting the 'editable' config option at config time.
17906 * @param {Boolean} value True to allow the user to directly edit the field text
17908 setEditable : function(value){
17909 if(value == this.editable){
17912 this.editable = value;
17914 this.inputEl().dom.setAttribute('readOnly', true);
17915 this.inputEl().on('mousedown', this.onTriggerClick, this);
17916 this.inputEl().addClass('x-combo-noedit');
17918 this.inputEl().dom.removeAttribute('readOnly');
17919 this.inputEl().un('mousedown', this.onTriggerClick, this);
17920 this.inputEl().removeClass('x-combo-noedit');
17926 onBeforeLoad : function(combo,opts){
17927 if(!this.hasFocus){
17931 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17933 this.restrictHeight();
17934 this.selectedIndex = -1;
17938 onLoad : function(){
17940 this.hasQuery = false;
17942 if(!this.hasFocus){
17946 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17947 this.loading.hide();
17950 if(this.store.getCount() > 0){
17953 this.restrictHeight();
17954 if(this.lastQuery == this.allQuery){
17955 if(this.editable && !this.tickable){
17956 this.inputEl().dom.select();
17960 !this.selectByValue(this.value, true) &&
17963 !this.store.lastOptions ||
17964 typeof(this.store.lastOptions.add) == 'undefined' ||
17965 this.store.lastOptions.add != true
17968 this.select(0, true);
17971 if(this.autoFocus){
17974 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17975 this.taTask.delay(this.typeAheadDelay);
17979 this.onEmptyResults();
17985 onLoadException : function()
17987 this.hasQuery = false;
17989 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17990 this.loading.hide();
17993 if(this.tickable && this.editable){
17998 // only causes errors at present
17999 //Roo.log(this.store.reader.jsonData);
18000 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18002 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18008 onTypeAhead : function(){
18009 if(this.store.getCount() > 0){
18010 var r = this.store.getAt(0);
18011 var newValue = r.data[this.displayField];
18012 var len = newValue.length;
18013 var selStart = this.getRawValue().length;
18015 if(selStart != len){
18016 this.setRawValue(newValue);
18017 this.selectText(selStart, newValue.length);
18023 onSelect : function(record, index){
18025 if(this.fireEvent('beforeselect', this, record, index) !== false){
18027 this.setFromData(index > -1 ? record.data : false);
18030 this.fireEvent('select', this, record, index);
18035 * Returns the currently selected field value or empty string if no value is set.
18036 * @return {String} value The selected value
18038 getValue : function()
18040 if(Roo.isIOS && this.useNativeIOS){
18041 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18045 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18048 if(this.valueField){
18049 return typeof this.value != 'undefined' ? this.value : '';
18051 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
18055 getRawValue : function()
18057 if(Roo.isIOS && this.useNativeIOS){
18058 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18061 var v = this.inputEl().getValue();
18067 * Clears any text/value currently set in the field
18069 clearValue : function(){
18071 if(this.hiddenField){
18072 this.hiddenField.dom.value = '';
18075 this.setRawValue('');
18076 this.lastSelectionText = '';
18077 this.lastData = false;
18079 var close = this.closeTriggerEl();
18090 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18091 * will be displayed in the field. If the value does not match the data value of an existing item,
18092 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18093 * Otherwise the field will be blank (although the value will still be set).
18094 * @param {String} value The value to match
18096 setValue : function(v)
18098 if(Roo.isIOS && this.useNativeIOS){
18099 this.setIOSValue(v);
18109 if(this.valueField){
18110 var r = this.findRecord(this.valueField, v);
18112 text = r.data[this.displayField];
18113 }else if(this.valueNotFoundText !== undefined){
18114 text = this.valueNotFoundText;
18117 this.lastSelectionText = text;
18118 if(this.hiddenField){
18119 this.hiddenField.dom.value = v;
18121 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
18124 var close = this.closeTriggerEl();
18127 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18133 * @property {Object} the last set data for the element
18138 * Sets the value of the field based on a object which is related to the record format for the store.
18139 * @param {Object} value the value to set as. or false on reset?
18141 setFromData : function(o){
18148 var dv = ''; // display value
18149 var vv = ''; // value value..
18151 if (this.displayField) {
18152 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18154 // this is an error condition!!!
18155 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18158 if(this.valueField){
18159 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18162 var close = this.closeTriggerEl();
18165 if(dv.length || vv * 1 > 0){
18167 this.blockFocus=true;
18173 if(this.hiddenField){
18174 this.hiddenField.dom.value = vv;
18176 this.lastSelectionText = dv;
18177 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
18181 // no hidden field.. - we store the value in 'value', but still display
18182 // display field!!!!
18183 this.lastSelectionText = dv;
18184 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
18191 reset : function(){
18192 // overridden so that last data is reset..
18199 this.setValue(this.originalValue);
18200 //this.clearInvalid();
18201 this.lastData = false;
18203 this.view.clearSelections();
18209 findRecord : function(prop, value){
18211 if(this.store.getCount() > 0){
18212 this.store.each(function(r){
18213 if(r.data[prop] == value){
18223 getName: function()
18225 // returns hidden if it's set..
18226 if (!this.rendered) {return ''};
18227 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18231 onViewMove : function(e, t){
18232 this.inKeyMode = false;
18236 onViewOver : function(e, t){
18237 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18240 var item = this.view.findItemFromChild(t);
18243 var index = this.view.indexOf(item);
18244 this.select(index, false);
18249 onViewClick : function(view, doFocus, el, e)
18251 var index = this.view.getSelectedIndexes()[0];
18253 var r = this.store.getAt(index);
18257 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18264 Roo.each(this.tickItems, function(v,k){
18266 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18268 _this.tickItems.splice(k, 1);
18270 if(typeof(e) == 'undefined' && view == false){
18271 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18283 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18284 this.tickItems.push(r.data);
18287 if(typeof(e) == 'undefined' && view == false){
18288 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18295 this.onSelect(r, index);
18297 if(doFocus !== false && !this.blockFocus){
18298 this.inputEl().focus();
18303 restrictHeight : function(){
18304 //this.innerList.dom.style.height = '';
18305 //var inner = this.innerList.dom;
18306 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18307 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18308 //this.list.beginUpdate();
18309 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18310 this.list.alignTo(this.inputEl(), this.listAlign);
18311 this.list.alignTo(this.inputEl(), this.listAlign);
18312 //this.list.endUpdate();
18316 onEmptyResults : function(){
18318 if(this.tickable && this.editable){
18319 this.hasFocus = false;
18320 this.restrictHeight();
18328 * Returns true if the dropdown list is expanded, else false.
18330 isExpanded : function(){
18331 return this.list.isVisible();
18335 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18336 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18337 * @param {String} value The data value of the item to select
18338 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18339 * selected item if it is not currently in view (defaults to true)
18340 * @return {Boolean} True if the value matched an item in the list, else false
18342 selectByValue : function(v, scrollIntoView){
18343 if(v !== undefined && v !== null){
18344 var r = this.findRecord(this.valueField || this.displayField, v);
18346 this.select(this.store.indexOf(r), scrollIntoView);
18354 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18355 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18356 * @param {Number} index The zero-based index of the list item to select
18357 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18358 * selected item if it is not currently in view (defaults to true)
18360 select : function(index, scrollIntoView){
18361 this.selectedIndex = index;
18362 this.view.select(index);
18363 if(scrollIntoView !== false){
18364 var el = this.view.getNode(index);
18366 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18369 this.list.scrollChildIntoView(el, false);
18375 selectNext : function(){
18376 var ct = this.store.getCount();
18378 if(this.selectedIndex == -1){
18380 }else if(this.selectedIndex < ct-1){
18381 this.select(this.selectedIndex+1);
18387 selectPrev : function(){
18388 var ct = this.store.getCount();
18390 if(this.selectedIndex == -1){
18392 }else if(this.selectedIndex != 0){
18393 this.select(this.selectedIndex-1);
18399 onKeyUp : function(e){
18400 if(this.editable !== false && !e.isSpecialKey()){
18401 this.lastKey = e.getKey();
18402 this.dqTask.delay(this.queryDelay);
18407 validateBlur : function(){
18408 return !this.list || !this.list.isVisible();
18412 initQuery : function(){
18414 var v = this.getRawValue();
18416 if(this.tickable && this.editable){
18417 v = this.tickableInputEl().getValue();
18424 doForce : function(){
18425 if(this.inputEl().dom.value.length > 0){
18426 this.inputEl().dom.value =
18427 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18433 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18434 * query allowing the query action to be canceled if needed.
18435 * @param {String} query The SQL query to execute
18436 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18437 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18438 * saved in the current store (defaults to false)
18440 doQuery : function(q, forceAll){
18442 if(q === undefined || q === null){
18447 forceAll: forceAll,
18451 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18456 forceAll = qe.forceAll;
18457 if(forceAll === true || (q.length >= this.minChars)){
18459 this.hasQuery = true;
18461 if(this.lastQuery != q || this.alwaysQuery){
18462 this.lastQuery = q;
18463 if(this.mode == 'local'){
18464 this.selectedIndex = -1;
18466 this.store.clearFilter();
18469 if(this.specialFilter){
18470 this.fireEvent('specialfilter', this);
18475 this.store.filter(this.displayField, q);
18478 this.store.fireEvent("datachanged", this.store);
18485 this.store.baseParams[this.queryParam] = q;
18487 var options = {params : this.getParams(q)};
18490 options.add = true;
18491 options.params.start = this.page * this.pageSize;
18494 this.store.load(options);
18497 * this code will make the page width larger, at the beginning, the list not align correctly,
18498 * we should expand the list on onLoad
18499 * so command out it
18504 this.selectedIndex = -1;
18509 this.loadNext = false;
18513 getParams : function(q){
18515 //p[this.queryParam] = q;
18519 p.limit = this.pageSize;
18525 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18527 collapse : function(){
18528 if(!this.isExpanded()){
18534 this.hasFocus = false;
18538 this.cancelBtn.hide();
18539 this.trigger.show();
18542 this.tickableInputEl().dom.value = '';
18543 this.tickableInputEl().blur();
18548 Roo.get(document).un('mousedown', this.collapseIf, this);
18549 Roo.get(document).un('mousewheel', this.collapseIf, this);
18550 if (!this.editable) {
18551 Roo.get(document).un('keydown', this.listKeyPress, this);
18553 this.fireEvent('collapse', this);
18559 collapseIf : function(e){
18560 var in_combo = e.within(this.el);
18561 var in_list = e.within(this.list);
18562 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18564 if (in_combo || in_list || is_list) {
18565 //e.stopPropagation();
18570 this.onTickableFooterButtonClick(e, false, false);
18578 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18580 expand : function(){
18582 if(this.isExpanded() || !this.hasFocus){
18586 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18587 this.list.setWidth(lw);
18593 this.restrictHeight();
18597 this.tickItems = Roo.apply([], this.item);
18600 this.cancelBtn.show();
18601 this.trigger.hide();
18604 this.tickableInputEl().focus();
18609 Roo.get(document).on('mousedown', this.collapseIf, this);
18610 Roo.get(document).on('mousewheel', this.collapseIf, this);
18611 if (!this.editable) {
18612 Roo.get(document).on('keydown', this.listKeyPress, this);
18615 this.fireEvent('expand', this);
18619 // Implements the default empty TriggerField.onTriggerClick function
18620 onTriggerClick : function(e)
18622 Roo.log('trigger click');
18624 if(this.disabled || !this.triggerList){
18629 this.loadNext = false;
18631 if(this.isExpanded()){
18633 if (!this.blockFocus) {
18634 this.inputEl().focus();
18638 this.hasFocus = true;
18639 if(this.triggerAction == 'all') {
18640 this.doQuery(this.allQuery, true);
18642 this.doQuery(this.getRawValue());
18644 if (!this.blockFocus) {
18645 this.inputEl().focus();
18650 onTickableTriggerClick : function(e)
18657 this.loadNext = false;
18658 this.hasFocus = true;
18660 if(this.triggerAction == 'all') {
18661 this.doQuery(this.allQuery, true);
18663 this.doQuery(this.getRawValue());
18667 onSearchFieldClick : function(e)
18669 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18670 this.onTickableFooterButtonClick(e, false, false);
18674 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18679 this.loadNext = false;
18680 this.hasFocus = true;
18682 if(this.triggerAction == 'all') {
18683 this.doQuery(this.allQuery, true);
18685 this.doQuery(this.getRawValue());
18689 listKeyPress : function(e)
18691 //Roo.log('listkeypress');
18692 // scroll to first matching element based on key pres..
18693 if (e.isSpecialKey()) {
18696 var k = String.fromCharCode(e.getKey()).toUpperCase();
18699 var csel = this.view.getSelectedNodes();
18700 var cselitem = false;
18702 var ix = this.view.indexOf(csel[0]);
18703 cselitem = this.store.getAt(ix);
18704 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18710 this.store.each(function(v) {
18712 // start at existing selection.
18713 if (cselitem.id == v.id) {
18719 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18720 match = this.store.indexOf(v);
18726 if (match === false) {
18727 return true; // no more action?
18730 this.view.select(match);
18731 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18732 sn.scrollIntoView(sn.dom.parentNode, false);
18735 onViewScroll : function(e, t){
18737 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){
18741 this.hasQuery = true;
18743 this.loading = this.list.select('.loading', true).first();
18745 if(this.loading === null){
18746 this.list.createChild({
18748 cls: 'loading roo-select2-more-results roo-select2-active',
18749 html: 'Loading more results...'
18752 this.loading = this.list.select('.loading', true).first();
18754 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18756 this.loading.hide();
18759 this.loading.show();
18764 this.loadNext = true;
18766 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18771 addItem : function(o)
18773 var dv = ''; // display value
18775 if (this.displayField) {
18776 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18778 // this is an error condition!!!
18779 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18786 var choice = this.choices.createChild({
18788 cls: 'roo-select2-search-choice',
18797 cls: 'roo-select2-search-choice-close fa fa-times',
18802 }, this.searchField);
18804 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18806 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18814 this.inputEl().dom.value = '';
18819 onRemoveItem : function(e, _self, o)
18821 e.preventDefault();
18823 this.lastItem = Roo.apply([], this.item);
18825 var index = this.item.indexOf(o.data) * 1;
18828 Roo.log('not this item?!');
18832 this.item.splice(index, 1);
18837 this.fireEvent('remove', this, e);
18843 syncValue : function()
18845 if(!this.item.length){
18852 Roo.each(this.item, function(i){
18853 if(_this.valueField){
18854 value.push(i[_this.valueField]);
18861 this.value = value.join(',');
18863 if(this.hiddenField){
18864 this.hiddenField.dom.value = this.value;
18867 this.store.fireEvent("datachanged", this.store);
18872 clearItem : function()
18874 if(!this.multiple){
18880 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18888 if(this.tickable && !Roo.isTouch){
18889 this.view.refresh();
18893 inputEl: function ()
18895 if(Roo.isIOS && this.useNativeIOS){
18896 return this.el.select('select.roo-ios-select', true).first();
18899 if(Roo.isTouch && this.mobileTouchView){
18900 return this.el.select('input.form-control',true).first();
18904 return this.searchField;
18907 return this.el.select('input.form-control',true).first();
18910 onTickableFooterButtonClick : function(e, btn, el)
18912 e.preventDefault();
18914 this.lastItem = Roo.apply([], this.item);
18916 if(btn && btn.name == 'cancel'){
18917 this.tickItems = Roo.apply([], this.item);
18926 Roo.each(this.tickItems, function(o){
18934 validate : function()
18936 if(this.getVisibilityEl().hasClass('hidden')){
18940 var v = this.getRawValue();
18943 v = this.getValue();
18946 if(this.disabled || this.allowBlank || v.length){
18951 this.markInvalid();
18955 tickableInputEl : function()
18957 if(!this.tickable || !this.editable){
18958 return this.inputEl();
18961 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18965 getAutoCreateTouchView : function()
18970 cls: 'form-group' //input-group
18976 type : this.inputType,
18977 cls : 'form-control x-combo-noedit',
18978 autocomplete: 'new-password',
18979 placeholder : this.placeholder || '',
18984 input.name = this.name;
18988 input.cls += ' input-' + this.size;
18991 if (this.disabled) {
18992 input.disabled = true;
18996 cls : 'roo-combobox-wrap',
19003 inputblock.cls += ' input-group';
19005 inputblock.cn.unshift({
19007 cls : 'input-group-addon input-group-prepend input-group-text',
19012 if(this.removable && !this.multiple){
19013 inputblock.cls += ' roo-removable';
19015 inputblock.cn.push({
19018 cls : 'roo-combo-removable-btn close'
19022 if(this.hasFeedback && !this.allowBlank){
19024 inputblock.cls += ' has-feedback';
19026 inputblock.cn.push({
19028 cls: 'glyphicon form-control-feedback'
19035 inputblock.cls += (this.before) ? '' : ' input-group';
19037 inputblock.cn.push({
19039 cls : 'input-group-addon input-group-append input-group-text',
19045 var ibwrap = inputblock;
19050 cls: 'roo-select2-choices',
19054 cls: 'roo-select2-search-field',
19067 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19072 cls: 'form-hidden-field'
19078 if(!this.multiple && this.showToggleBtn){
19084 if (this.caret != false) {
19087 cls: 'fa fa-' + this.caret
19094 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19096 Roo.bootstrap.version == 3 ? caret : '',
19099 cls: 'combobox-clear',
19113 combobox.cls += ' roo-select2-container-multi';
19116 var required = this.allowBlank ? {
19118 style: 'display: none'
19121 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19122 tooltip : 'This field is required'
19125 var align = this.labelAlign || this.parentLabelAlign();
19127 if (align ==='left' && this.fieldLabel.length) {
19133 cls : 'control-label col-form-label',
19134 html : this.fieldLabel
19138 cls : 'roo-combobox-wrap ',
19145 var labelCfg = cfg.cn[1];
19146 var contentCfg = cfg.cn[2];
19149 if(this.indicatorpos == 'right'){
19154 cls : 'control-label col-form-label',
19158 html : this.fieldLabel
19164 cls : "roo-combobox-wrap ",
19172 labelCfg = cfg.cn[0];
19173 contentCfg = cfg.cn[1];
19178 if(this.labelWidth > 12){
19179 labelCfg.style = "width: " + this.labelWidth + 'px';
19182 if(this.labelWidth < 13 && this.labelmd == 0){
19183 this.labelmd = this.labelWidth;
19186 if(this.labellg > 0){
19187 labelCfg.cls += ' col-lg-' + this.labellg;
19188 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19191 if(this.labelmd > 0){
19192 labelCfg.cls += ' col-md-' + this.labelmd;
19193 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19196 if(this.labelsm > 0){
19197 labelCfg.cls += ' col-sm-' + this.labelsm;
19198 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19201 if(this.labelxs > 0){
19202 labelCfg.cls += ' col-xs-' + this.labelxs;
19203 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19207 } else if ( this.fieldLabel.length) {
19212 cls : 'control-label',
19213 html : this.fieldLabel
19224 if(this.indicatorpos == 'right'){
19228 cls : 'control-label',
19229 html : this.fieldLabel,
19247 var settings = this;
19249 ['xs','sm','md','lg'].map(function(size){
19250 if (settings[size]) {
19251 cfg.cls += ' col-' + size + '-' + settings[size];
19258 initTouchView : function()
19260 this.renderTouchView();
19262 this.touchViewEl.on('scroll', function(){
19263 this.el.dom.scrollTop = 0;
19266 this.originalValue = this.getValue();
19268 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19270 this.inputEl().on("click", this.showTouchView, this);
19271 if (this.triggerEl) {
19272 this.triggerEl.on("click", this.showTouchView, this);
19276 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19277 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19279 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19281 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19282 this.store.on('load', this.onTouchViewLoad, this);
19283 this.store.on('loadexception', this.onTouchViewLoadException, this);
19285 if(this.hiddenName){
19287 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19289 this.hiddenField.dom.value =
19290 this.hiddenValue !== undefined ? this.hiddenValue :
19291 this.value !== undefined ? this.value : '';
19293 this.el.dom.removeAttribute('name');
19294 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19298 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19299 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19302 if(this.removable && !this.multiple){
19303 var close = this.closeTriggerEl();
19305 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19306 close.on('click', this.removeBtnClick, this, close);
19310 * fix the bug in Safari iOS8
19312 this.inputEl().on("focus", function(e){
19313 document.activeElement.blur();
19316 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19323 renderTouchView : function()
19325 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
19326 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19328 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19329 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19331 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19332 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19333 this.touchViewBodyEl.setStyle('overflow', 'auto');
19335 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19336 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19338 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19339 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19343 showTouchView : function()
19349 this.touchViewHeaderEl.hide();
19351 if(this.modalTitle.length){
19352 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19353 this.touchViewHeaderEl.show();
19356 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19357 this.touchViewEl.show();
19359 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19361 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19362 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19364 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19366 if(this.modalTitle.length){
19367 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19370 this.touchViewBodyEl.setHeight(bodyHeight);
19374 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19376 this.touchViewEl.addClass(['in','show']);
19379 if(this._touchViewMask){
19380 Roo.get(document.body).addClass("x-body-masked");
19381 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19382 this._touchViewMask.setStyle('z-index', 10000);
19383 this._touchViewMask.addClass('show');
19386 this.doTouchViewQuery();
19390 hideTouchView : function()
19392 this.touchViewEl.removeClass(['in','show']);
19396 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19398 this.touchViewEl.setStyle('display', 'none');
19401 if(this._touchViewMask){
19402 this._touchViewMask.removeClass('show');
19403 Roo.get(document.body).removeClass("x-body-masked");
19407 setTouchViewValue : function()
19414 Roo.each(this.tickItems, function(o){
19419 this.hideTouchView();
19422 doTouchViewQuery : function()
19431 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19435 if(!this.alwaysQuery || this.mode == 'local'){
19436 this.onTouchViewLoad();
19443 onTouchViewBeforeLoad : function(combo,opts)
19449 onTouchViewLoad : function()
19451 if(this.store.getCount() < 1){
19452 this.onTouchViewEmptyResults();
19456 this.clearTouchView();
19458 var rawValue = this.getRawValue();
19460 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19462 this.tickItems = [];
19464 this.store.data.each(function(d, rowIndex){
19465 var row = this.touchViewListGroup.createChild(template);
19467 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19468 row.addClass(d.data.cls);
19471 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19474 html : d.data[this.displayField]
19477 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19478 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19481 row.removeClass('selected');
19482 if(!this.multiple && this.valueField &&
19483 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19486 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19487 row.addClass('selected');
19490 if(this.multiple && this.valueField &&
19491 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19495 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19496 this.tickItems.push(d.data);
19499 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19503 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19505 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19507 if(this.modalTitle.length){
19508 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19511 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19513 if(this.mobile_restrict_height && listHeight < bodyHeight){
19514 this.touchViewBodyEl.setHeight(listHeight);
19519 if(firstChecked && listHeight > bodyHeight){
19520 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19525 onTouchViewLoadException : function()
19527 this.hideTouchView();
19530 onTouchViewEmptyResults : function()
19532 this.clearTouchView();
19534 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19536 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19540 clearTouchView : function()
19542 this.touchViewListGroup.dom.innerHTML = '';
19545 onTouchViewClick : function(e, el, o)
19547 e.preventDefault();
19550 var rowIndex = o.rowIndex;
19552 var r = this.store.getAt(rowIndex);
19554 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19556 if(!this.multiple){
19557 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19558 c.dom.removeAttribute('checked');
19561 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19563 this.setFromData(r.data);
19565 var close = this.closeTriggerEl();
19571 this.hideTouchView();
19573 this.fireEvent('select', this, r, rowIndex);
19578 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19579 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19580 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19584 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19585 this.addItem(r.data);
19586 this.tickItems.push(r.data);
19590 getAutoCreateNativeIOS : function()
19593 cls: 'form-group' //input-group,
19598 cls : 'roo-ios-select'
19602 combobox.name = this.name;
19605 if (this.disabled) {
19606 combobox.disabled = true;
19609 var settings = this;
19611 ['xs','sm','md','lg'].map(function(size){
19612 if (settings[size]) {
19613 cfg.cls += ' col-' + size + '-' + settings[size];
19623 initIOSView : function()
19625 this.store.on('load', this.onIOSViewLoad, this);
19630 onIOSViewLoad : function()
19632 if(this.store.getCount() < 1){
19636 this.clearIOSView();
19638 if(this.allowBlank) {
19640 var default_text = '-- SELECT --';
19642 if(this.placeholder.length){
19643 default_text = this.placeholder;
19646 if(this.emptyTitle.length){
19647 default_text += ' - ' + this.emptyTitle + ' -';
19650 var opt = this.inputEl().createChild({
19653 html : default_text
19657 o[this.valueField] = 0;
19658 o[this.displayField] = default_text;
19660 this.ios_options.push({
19667 this.store.data.each(function(d, rowIndex){
19671 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19672 html = d.data[this.displayField];
19677 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19678 value = d.data[this.valueField];
19687 if(this.value == d.data[this.valueField]){
19688 option['selected'] = true;
19691 var opt = this.inputEl().createChild(option);
19693 this.ios_options.push({
19700 this.inputEl().on('change', function(){
19701 this.fireEvent('select', this);
19706 clearIOSView: function()
19708 this.inputEl().dom.innerHTML = '';
19710 this.ios_options = [];
19713 setIOSValue: function(v)
19717 if(!this.ios_options){
19721 Roo.each(this.ios_options, function(opts){
19723 opts.el.dom.removeAttribute('selected');
19725 if(opts.data[this.valueField] != v){
19729 opts.el.dom.setAttribute('selected', true);
19735 * @cfg {Boolean} grow
19739 * @cfg {Number} growMin
19743 * @cfg {Number} growMax
19752 Roo.apply(Roo.bootstrap.ComboBox, {
19756 cls: 'modal-header',
19778 cls: 'list-group-item',
19782 cls: 'roo-combobox-list-group-item-value'
19786 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19800 listItemCheckbox : {
19802 cls: 'list-group-item',
19806 cls: 'roo-combobox-list-group-item-value'
19810 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19826 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19831 cls: 'modal-footer',
19839 cls: 'col-xs-6 text-left',
19842 cls: 'btn btn-danger roo-touch-view-cancel',
19848 cls: 'col-xs-6 text-right',
19851 cls: 'btn btn-success roo-touch-view-ok',
19862 Roo.apply(Roo.bootstrap.ComboBox, {
19864 touchViewTemplate : {
19866 cls: 'modal fade roo-combobox-touch-view',
19870 cls: 'modal-dialog',
19871 style : 'position:fixed', // we have to fix position....
19875 cls: 'modal-content',
19877 Roo.bootstrap.ComboBox.header,
19878 Roo.bootstrap.ComboBox.body,
19879 Roo.bootstrap.ComboBox.footer
19888 * Ext JS Library 1.1.1
19889 * Copyright(c) 2006-2007, Ext JS, LLC.
19891 * Originally Released Under LGPL - original licence link has changed is not relivant.
19894 * <script type="text/javascript">
19899 * @extends Roo.util.Observable
19900 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19901 * This class also supports single and multi selection modes. <br>
19902 * Create a data model bound view:
19904 var store = new Roo.data.Store(...);
19906 var view = new Roo.View({
19908 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19910 singleSelect: true,
19911 selectedClass: "ydataview-selected",
19915 // listen for node click?
19916 view.on("click", function(vw, index, node, e){
19917 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19921 dataModel.load("foobar.xml");
19923 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19925 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19926 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19928 * Note: old style constructor is still suported (container, template, config)
19931 * Create a new View
19932 * @param {Object} config The config object
19935 Roo.View = function(config, depreciated_tpl, depreciated_config){
19937 this.parent = false;
19939 if (typeof(depreciated_tpl) == 'undefined') {
19940 // new way.. - universal constructor.
19941 Roo.apply(this, config);
19942 this.el = Roo.get(this.el);
19945 this.el = Roo.get(config);
19946 this.tpl = depreciated_tpl;
19947 Roo.apply(this, depreciated_config);
19949 this.wrapEl = this.el.wrap().wrap();
19950 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19953 if(typeof(this.tpl) == "string"){
19954 this.tpl = new Roo.Template(this.tpl);
19956 // support xtype ctors..
19957 this.tpl = new Roo.factory(this.tpl, Roo);
19961 this.tpl.compile();
19966 * @event beforeclick
19967 * Fires before a click is processed. Returns false to cancel the default action.
19968 * @param {Roo.View} this
19969 * @param {Number} index The index of the target node
19970 * @param {HTMLElement} node The target node
19971 * @param {Roo.EventObject} e The raw event object
19973 "beforeclick" : true,
19976 * Fires when a template node is clicked.
19977 * @param {Roo.View} this
19978 * @param {Number} index The index of the target node
19979 * @param {HTMLElement} node The target node
19980 * @param {Roo.EventObject} e The raw event object
19985 * Fires when a template node is double clicked.
19986 * @param {Roo.View} this
19987 * @param {Number} index The index of the target node
19988 * @param {HTMLElement} node The target node
19989 * @param {Roo.EventObject} e The raw event object
19993 * @event contextmenu
19994 * Fires when a template node is right clicked.
19995 * @param {Roo.View} this
19996 * @param {Number} index The index of the target node
19997 * @param {HTMLElement} node The target node
19998 * @param {Roo.EventObject} e The raw event object
20000 "contextmenu" : true,
20002 * @event selectionchange
20003 * Fires when the selected nodes change.
20004 * @param {Roo.View} this
20005 * @param {Array} selections Array of the selected nodes
20007 "selectionchange" : true,
20010 * @event beforeselect
20011 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20012 * @param {Roo.View} this
20013 * @param {HTMLElement} node The node to be selected
20014 * @param {Array} selections Array of currently selected nodes
20016 "beforeselect" : true,
20018 * @event preparedata
20019 * Fires on every row to render, to allow you to change the data.
20020 * @param {Roo.View} this
20021 * @param {Object} data to be rendered (change this)
20023 "preparedata" : true
20031 "click": this.onClick,
20032 "dblclick": this.onDblClick,
20033 "contextmenu": this.onContextMenu,
20037 this.selections = [];
20039 this.cmp = new Roo.CompositeElementLite([]);
20041 this.store = Roo.factory(this.store, Roo.data);
20042 this.setStore(this.store, true);
20045 if ( this.footer && this.footer.xtype) {
20047 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20049 this.footer.dataSource = this.store;
20050 this.footer.container = fctr;
20051 this.footer = Roo.factory(this.footer, Roo);
20052 fctr.insertFirst(this.el);
20054 // this is a bit insane - as the paging toolbar seems to detach the el..
20055 // dom.parentNode.parentNode.parentNode
20056 // they get detached?
20060 Roo.View.superclass.constructor.call(this);
20065 Roo.extend(Roo.View, Roo.util.Observable, {
20068 * @cfg {Roo.data.Store} store Data store to load data from.
20073 * @cfg {String|Roo.Element} el The container element.
20078 * @cfg {String|Roo.Template} tpl The template used by this View
20082 * @cfg {String} dataName the named area of the template to use as the data area
20083 * Works with domtemplates roo-name="name"
20087 * @cfg {String} selectedClass The css class to add to selected nodes
20089 selectedClass : "x-view-selected",
20091 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20096 * @cfg {String} text to display on mask (default Loading)
20100 * @cfg {Boolean} multiSelect Allow multiple selection
20102 multiSelect : false,
20104 * @cfg {Boolean} singleSelect Allow single selection
20106 singleSelect: false,
20109 * @cfg {Boolean} toggleSelect - selecting
20111 toggleSelect : false,
20114 * @cfg {Boolean} tickable - selecting
20119 * Returns the element this view is bound to.
20120 * @return {Roo.Element}
20122 getEl : function(){
20123 return this.wrapEl;
20129 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20131 refresh : function(){
20132 //Roo.log('refresh');
20135 // if we are using something like 'domtemplate', then
20136 // the what gets used is:
20137 // t.applySubtemplate(NAME, data, wrapping data..)
20138 // the outer template then get' applied with
20139 // the store 'extra data'
20140 // and the body get's added to the
20141 // roo-name="data" node?
20142 // <span class='roo-tpl-{name}'></span> ?????
20146 this.clearSelections();
20147 this.el.update("");
20149 var records = this.store.getRange();
20150 if(records.length < 1) {
20152 // is this valid?? = should it render a template??
20154 this.el.update(this.emptyText);
20158 if (this.dataName) {
20159 this.el.update(t.apply(this.store.meta)); //????
20160 el = this.el.child('.roo-tpl-' + this.dataName);
20163 for(var i = 0, len = records.length; i < len; i++){
20164 var data = this.prepareData(records[i].data, i, records[i]);
20165 this.fireEvent("preparedata", this, data, i, records[i]);
20167 var d = Roo.apply({}, data);
20170 Roo.apply(d, {'roo-id' : Roo.id()});
20174 Roo.each(this.parent.item, function(item){
20175 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20178 Roo.apply(d, {'roo-data-checked' : 'checked'});
20182 html[html.length] = Roo.util.Format.trim(
20184 t.applySubtemplate(this.dataName, d, this.store.meta) :
20191 el.update(html.join(""));
20192 this.nodes = el.dom.childNodes;
20193 this.updateIndexes(0);
20198 * Function to override to reformat the data that is sent to
20199 * the template for each node.
20200 * DEPRICATED - use the preparedata event handler.
20201 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20202 * a JSON object for an UpdateManager bound view).
20204 prepareData : function(data, index, record)
20206 this.fireEvent("preparedata", this, data, index, record);
20210 onUpdate : function(ds, record){
20211 // Roo.log('on update');
20212 this.clearSelections();
20213 var index = this.store.indexOf(record);
20214 var n = this.nodes[index];
20215 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20216 n.parentNode.removeChild(n);
20217 this.updateIndexes(index, index);
20223 onAdd : function(ds, records, index)
20225 //Roo.log(['on Add', ds, records, index] );
20226 this.clearSelections();
20227 if(this.nodes.length == 0){
20231 var n = this.nodes[index];
20232 for(var i = 0, len = records.length; i < len; i++){
20233 var d = this.prepareData(records[i].data, i, records[i]);
20235 this.tpl.insertBefore(n, d);
20238 this.tpl.append(this.el, d);
20241 this.updateIndexes(index);
20244 onRemove : function(ds, record, index){
20245 // Roo.log('onRemove');
20246 this.clearSelections();
20247 var el = this.dataName ?
20248 this.el.child('.roo-tpl-' + this.dataName) :
20251 el.dom.removeChild(this.nodes[index]);
20252 this.updateIndexes(index);
20256 * Refresh an individual node.
20257 * @param {Number} index
20259 refreshNode : function(index){
20260 this.onUpdate(this.store, this.store.getAt(index));
20263 updateIndexes : function(startIndex, endIndex){
20264 var ns = this.nodes;
20265 startIndex = startIndex || 0;
20266 endIndex = endIndex || ns.length - 1;
20267 for(var i = startIndex; i <= endIndex; i++){
20268 ns[i].nodeIndex = i;
20273 * Changes the data store this view uses and refresh the view.
20274 * @param {Store} store
20276 setStore : function(store, initial){
20277 if(!initial && this.store){
20278 this.store.un("datachanged", this.refresh);
20279 this.store.un("add", this.onAdd);
20280 this.store.un("remove", this.onRemove);
20281 this.store.un("update", this.onUpdate);
20282 this.store.un("clear", this.refresh);
20283 this.store.un("beforeload", this.onBeforeLoad);
20284 this.store.un("load", this.onLoad);
20285 this.store.un("loadexception", this.onLoad);
20289 store.on("datachanged", this.refresh, this);
20290 store.on("add", this.onAdd, this);
20291 store.on("remove", this.onRemove, this);
20292 store.on("update", this.onUpdate, this);
20293 store.on("clear", this.refresh, this);
20294 store.on("beforeload", this.onBeforeLoad, this);
20295 store.on("load", this.onLoad, this);
20296 store.on("loadexception", this.onLoad, this);
20304 * onbeforeLoad - masks the loading area.
20307 onBeforeLoad : function(store,opts)
20309 //Roo.log('onBeforeLoad');
20311 this.el.update("");
20313 this.el.mask(this.mask ? this.mask : "Loading" );
20315 onLoad : function ()
20322 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20323 * @param {HTMLElement} node
20324 * @return {HTMLElement} The template node
20326 findItemFromChild : function(node){
20327 var el = this.dataName ?
20328 this.el.child('.roo-tpl-' + this.dataName,true) :
20331 if(!node || node.parentNode == el){
20334 var p = node.parentNode;
20335 while(p && p != el){
20336 if(p.parentNode == el){
20345 onClick : function(e){
20346 var item = this.findItemFromChild(e.getTarget());
20348 var index = this.indexOf(item);
20349 if(this.onItemClick(item, index, e) !== false){
20350 this.fireEvent("click", this, index, item, e);
20353 this.clearSelections();
20358 onContextMenu : function(e){
20359 var item = this.findItemFromChild(e.getTarget());
20361 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20366 onDblClick : function(e){
20367 var item = this.findItemFromChild(e.getTarget());
20369 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20373 onItemClick : function(item, index, e)
20375 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20378 if (this.toggleSelect) {
20379 var m = this.isSelected(item) ? 'unselect' : 'select';
20382 _t[m](item, true, false);
20385 if(this.multiSelect || this.singleSelect){
20386 if(this.multiSelect && e.shiftKey && this.lastSelection){
20387 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20389 this.select(item, this.multiSelect && e.ctrlKey);
20390 this.lastSelection = item;
20393 if(!this.tickable){
20394 e.preventDefault();
20402 * Get the number of selected nodes.
20405 getSelectionCount : function(){
20406 return this.selections.length;
20410 * Get the currently selected nodes.
20411 * @return {Array} An array of HTMLElements
20413 getSelectedNodes : function(){
20414 return this.selections;
20418 * Get the indexes of the selected nodes.
20421 getSelectedIndexes : function(){
20422 var indexes = [], s = this.selections;
20423 for(var i = 0, len = s.length; i < len; i++){
20424 indexes.push(s[i].nodeIndex);
20430 * Clear all selections
20431 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20433 clearSelections : function(suppressEvent){
20434 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20435 this.cmp.elements = this.selections;
20436 this.cmp.removeClass(this.selectedClass);
20437 this.selections = [];
20438 if(!suppressEvent){
20439 this.fireEvent("selectionchange", this, this.selections);
20445 * Returns true if the passed node is selected
20446 * @param {HTMLElement/Number} node The node or node index
20447 * @return {Boolean}
20449 isSelected : function(node){
20450 var s = this.selections;
20454 node = this.getNode(node);
20455 return s.indexOf(node) !== -1;
20460 * @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
20461 * @param {Boolean} keepExisting (optional) true to keep existing selections
20462 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20464 select : function(nodeInfo, keepExisting, suppressEvent){
20465 if(nodeInfo instanceof Array){
20467 this.clearSelections(true);
20469 for(var i = 0, len = nodeInfo.length; i < len; i++){
20470 this.select(nodeInfo[i], true, true);
20474 var node = this.getNode(nodeInfo);
20475 if(!node || this.isSelected(node)){
20476 return; // already selected.
20479 this.clearSelections(true);
20482 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20483 Roo.fly(node).addClass(this.selectedClass);
20484 this.selections.push(node);
20485 if(!suppressEvent){
20486 this.fireEvent("selectionchange", this, this.selections);
20494 * @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
20495 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20496 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20498 unselect : function(nodeInfo, keepExisting, suppressEvent)
20500 if(nodeInfo instanceof Array){
20501 Roo.each(this.selections, function(s) {
20502 this.unselect(s, nodeInfo);
20506 var node = this.getNode(nodeInfo);
20507 if(!node || !this.isSelected(node)){
20508 //Roo.log("not selected");
20509 return; // not selected.
20513 Roo.each(this.selections, function(s) {
20515 Roo.fly(node).removeClass(this.selectedClass);
20522 this.selections= ns;
20523 this.fireEvent("selectionchange", this, this.selections);
20527 * Gets a template node.
20528 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20529 * @return {HTMLElement} The node or null if it wasn't found
20531 getNode : function(nodeInfo){
20532 if(typeof nodeInfo == "string"){
20533 return document.getElementById(nodeInfo);
20534 }else if(typeof nodeInfo == "number"){
20535 return this.nodes[nodeInfo];
20541 * Gets a range template nodes.
20542 * @param {Number} startIndex
20543 * @param {Number} endIndex
20544 * @return {Array} An array of nodes
20546 getNodes : function(start, end){
20547 var ns = this.nodes;
20548 start = start || 0;
20549 end = typeof end == "undefined" ? ns.length - 1 : end;
20552 for(var i = start; i <= end; i++){
20556 for(var i = start; i >= end; i--){
20564 * Finds the index of the passed node
20565 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20566 * @return {Number} The index of the node or -1
20568 indexOf : function(node){
20569 node = this.getNode(node);
20570 if(typeof node.nodeIndex == "number"){
20571 return node.nodeIndex;
20573 var ns = this.nodes;
20574 for(var i = 0, len = ns.length; i < len; i++){
20585 * based on jquery fullcalendar
20589 Roo.bootstrap = Roo.bootstrap || {};
20591 * @class Roo.bootstrap.Calendar
20592 * @extends Roo.bootstrap.Component
20593 * Bootstrap Calendar class
20594 * @cfg {Boolean} loadMask (true|false) default false
20595 * @cfg {Object} header generate the user specific header of the calendar, default false
20598 * Create a new Container
20599 * @param {Object} config The config object
20604 Roo.bootstrap.Calendar = function(config){
20605 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20609 * Fires when a date is selected
20610 * @param {DatePicker} this
20611 * @param {Date} date The selected date
20615 * @event monthchange
20616 * Fires when the displayed month changes
20617 * @param {DatePicker} this
20618 * @param {Date} date The selected month
20620 'monthchange': true,
20622 * @event evententer
20623 * Fires when mouse over an event
20624 * @param {Calendar} this
20625 * @param {event} Event
20627 'evententer': true,
20629 * @event eventleave
20630 * Fires when the mouse leaves an
20631 * @param {Calendar} this
20634 'eventleave': true,
20636 * @event eventclick
20637 * Fires when the mouse click an
20638 * @param {Calendar} this
20647 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20650 * @cfg {Roo.data.Store} store
20651 * The data source for the calendar
20655 * @cfg {Number} startDay
20656 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20664 getAutoCreate : function(){
20667 var fc_button = function(name, corner, style, content ) {
20668 return Roo.apply({},{
20670 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20672 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20675 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20686 style : 'width:100%',
20693 cls : 'fc-header-left',
20695 fc_button('prev', 'left', 'arrow', '‹' ),
20696 fc_button('next', 'right', 'arrow', '›' ),
20697 { tag: 'span', cls: 'fc-header-space' },
20698 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20706 cls : 'fc-header-center',
20710 cls: 'fc-header-title',
20713 html : 'month / year'
20721 cls : 'fc-header-right',
20723 /* fc_button('month', 'left', '', 'month' ),
20724 fc_button('week', '', '', 'week' ),
20725 fc_button('day', 'right', '', 'day' )
20737 header = this.header;
20740 var cal_heads = function() {
20742 // fixme - handle this.
20744 for (var i =0; i < Date.dayNames.length; i++) {
20745 var d = Date.dayNames[i];
20748 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20749 html : d.substring(0,3)
20753 ret[0].cls += ' fc-first';
20754 ret[6].cls += ' fc-last';
20757 var cal_cell = function(n) {
20760 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20765 cls: 'fc-day-number',
20769 cls: 'fc-day-content',
20773 style: 'position: relative;' // height: 17px;
20785 var cal_rows = function() {
20788 for (var r = 0; r < 6; r++) {
20795 for (var i =0; i < Date.dayNames.length; i++) {
20796 var d = Date.dayNames[i];
20797 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20800 row.cn[0].cls+=' fc-first';
20801 row.cn[0].cn[0].style = 'min-height:90px';
20802 row.cn[6].cls+=' fc-last';
20806 ret[0].cls += ' fc-first';
20807 ret[4].cls += ' fc-prev-last';
20808 ret[5].cls += ' fc-last';
20815 cls: 'fc-border-separate',
20816 style : 'width:100%',
20824 cls : 'fc-first fc-last',
20842 cls : 'fc-content',
20843 style : "position: relative;",
20846 cls : 'fc-view fc-view-month fc-grid',
20847 style : 'position: relative',
20848 unselectable : 'on',
20851 cls : 'fc-event-container',
20852 style : 'position:absolute;z-index:8;top:0;left:0;'
20870 initEvents : function()
20873 throw "can not find store for calendar";
20879 style: "text-align:center",
20883 style: "background-color:white;width:50%;margin:250 auto",
20887 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20898 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20900 var size = this.el.select('.fc-content', true).first().getSize();
20901 this.maskEl.setSize(size.width, size.height);
20902 this.maskEl.enableDisplayMode("block");
20903 if(!this.loadMask){
20904 this.maskEl.hide();
20907 this.store = Roo.factory(this.store, Roo.data);
20908 this.store.on('load', this.onLoad, this);
20909 this.store.on('beforeload', this.onBeforeLoad, this);
20913 this.cells = this.el.select('.fc-day',true);
20914 //Roo.log(this.cells);
20915 this.textNodes = this.el.query('.fc-day-number');
20916 this.cells.addClassOnOver('fc-state-hover');
20918 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20919 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20920 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20921 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20923 this.on('monthchange', this.onMonthChange, this);
20925 this.update(new Date().clearTime());
20928 resize : function() {
20929 var sz = this.el.getSize();
20931 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20932 this.el.select('.fc-day-content div',true).setHeight(34);
20937 showPrevMonth : function(e){
20938 this.update(this.activeDate.add("mo", -1));
20940 showToday : function(e){
20941 this.update(new Date().clearTime());
20944 showNextMonth : function(e){
20945 this.update(this.activeDate.add("mo", 1));
20949 showPrevYear : function(){
20950 this.update(this.activeDate.add("y", -1));
20954 showNextYear : function(){
20955 this.update(this.activeDate.add("y", 1));
20960 update : function(date)
20962 var vd = this.activeDate;
20963 this.activeDate = date;
20964 // if(vd && this.el){
20965 // var t = date.getTime();
20966 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20967 // Roo.log('using add remove');
20969 // this.fireEvent('monthchange', this, date);
20971 // this.cells.removeClass("fc-state-highlight");
20972 // this.cells.each(function(c){
20973 // if(c.dateValue == t){
20974 // c.addClass("fc-state-highlight");
20975 // setTimeout(function(){
20976 // try{c.dom.firstChild.focus();}catch(e){}
20986 var days = date.getDaysInMonth();
20988 var firstOfMonth = date.getFirstDateOfMonth();
20989 var startingPos = firstOfMonth.getDay()-this.startDay;
20991 if(startingPos < this.startDay){
20995 var pm = date.add(Date.MONTH, -1);
20996 var prevStart = pm.getDaysInMonth()-startingPos;
20998 this.cells = this.el.select('.fc-day',true);
20999 this.textNodes = this.el.query('.fc-day-number');
21000 this.cells.addClassOnOver('fc-state-hover');
21002 var cells = this.cells.elements;
21003 var textEls = this.textNodes;
21005 Roo.each(cells, function(cell){
21006 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21009 days += startingPos;
21011 // convert everything to numbers so it's fast
21012 var day = 86400000;
21013 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21016 //Roo.log(prevStart);
21018 var today = new Date().clearTime().getTime();
21019 var sel = date.clearTime().getTime();
21020 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21021 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21022 var ddMatch = this.disabledDatesRE;
21023 var ddText = this.disabledDatesText;
21024 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21025 var ddaysText = this.disabledDaysText;
21026 var format = this.format;
21028 var setCellClass = function(cal, cell){
21032 //Roo.log('set Cell Class');
21034 var t = d.getTime();
21038 cell.dateValue = t;
21040 cell.className += " fc-today";
21041 cell.className += " fc-state-highlight";
21042 cell.title = cal.todayText;
21045 // disable highlight in other month..
21046 //cell.className += " fc-state-highlight";
21051 cell.className = " fc-state-disabled";
21052 cell.title = cal.minText;
21056 cell.className = " fc-state-disabled";
21057 cell.title = cal.maxText;
21061 if(ddays.indexOf(d.getDay()) != -1){
21062 cell.title = ddaysText;
21063 cell.className = " fc-state-disabled";
21066 if(ddMatch && format){
21067 var fvalue = d.dateFormat(format);
21068 if(ddMatch.test(fvalue)){
21069 cell.title = ddText.replace("%0", fvalue);
21070 cell.className = " fc-state-disabled";
21074 if (!cell.initialClassName) {
21075 cell.initialClassName = cell.dom.className;
21078 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21083 for(; i < startingPos; i++) {
21084 textEls[i].innerHTML = (++prevStart);
21085 d.setDate(d.getDate()+1);
21087 cells[i].className = "fc-past fc-other-month";
21088 setCellClass(this, cells[i]);
21093 for(; i < days; i++){
21094 intDay = i - startingPos + 1;
21095 textEls[i].innerHTML = (intDay);
21096 d.setDate(d.getDate()+1);
21098 cells[i].className = ''; // "x-date-active";
21099 setCellClass(this, cells[i]);
21103 for(; i < 42; i++) {
21104 textEls[i].innerHTML = (++extraDays);
21105 d.setDate(d.getDate()+1);
21107 cells[i].className = "fc-future fc-other-month";
21108 setCellClass(this, cells[i]);
21111 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21113 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21115 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21116 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21118 if(totalRows != 6){
21119 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21120 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21123 this.fireEvent('monthchange', this, date);
21127 if(!this.internalRender){
21128 var main = this.el.dom.firstChild;
21129 var w = main.offsetWidth;
21130 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21131 Roo.fly(main).setWidth(w);
21132 this.internalRender = true;
21133 // opera does not respect the auto grow header center column
21134 // then, after it gets a width opera refuses to recalculate
21135 // without a second pass
21136 if(Roo.isOpera && !this.secondPass){
21137 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21138 this.secondPass = true;
21139 this.update.defer(10, this, [date]);
21146 findCell : function(dt) {
21147 dt = dt.clearTime().getTime();
21149 this.cells.each(function(c){
21150 //Roo.log("check " +c.dateValue + '?=' + dt);
21151 if(c.dateValue == dt){
21161 findCells : function(ev) {
21162 var s = ev.start.clone().clearTime().getTime();
21164 var e= ev.end.clone().clearTime().getTime();
21167 this.cells.each(function(c){
21168 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21170 if(c.dateValue > e){
21173 if(c.dateValue < s){
21182 // findBestRow: function(cells)
21186 // for (var i =0 ; i < cells.length;i++) {
21187 // ret = Math.max(cells[i].rows || 0,ret);
21194 addItem : function(ev)
21196 // look for vertical location slot in
21197 var cells = this.findCells(ev);
21199 // ev.row = this.findBestRow(cells);
21201 // work out the location.
21205 for(var i =0; i < cells.length; i++) {
21207 cells[i].row = cells[0].row;
21210 cells[i].row = cells[i].row + 1;
21220 if (crow.start.getY() == cells[i].getY()) {
21222 crow.end = cells[i];
21239 cells[0].events.push(ev);
21241 this.calevents.push(ev);
21244 clearEvents: function() {
21246 if(!this.calevents){
21250 Roo.each(this.cells.elements, function(c){
21256 Roo.each(this.calevents, function(e) {
21257 Roo.each(e.els, function(el) {
21258 el.un('mouseenter' ,this.onEventEnter, this);
21259 el.un('mouseleave' ,this.onEventLeave, this);
21264 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21270 renderEvents: function()
21274 this.cells.each(function(c) {
21283 if(c.row != c.events.length){
21284 r = 4 - (4 - (c.row - c.events.length));
21287 c.events = ev.slice(0, r);
21288 c.more = ev.slice(r);
21290 if(c.more.length && c.more.length == 1){
21291 c.events.push(c.more.pop());
21294 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21298 this.cells.each(function(c) {
21300 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21303 for (var e = 0; e < c.events.length; e++){
21304 var ev = c.events[e];
21305 var rows = ev.rows;
21307 for(var i = 0; i < rows.length; i++) {
21309 // how many rows should it span..
21312 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21313 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21315 unselectable : "on",
21318 cls: 'fc-event-inner',
21322 // cls: 'fc-event-time',
21323 // html : cells.length > 1 ? '' : ev.time
21327 cls: 'fc-event-title',
21328 html : String.format('{0}', ev.title)
21335 cls: 'ui-resizable-handle ui-resizable-e',
21336 html : '  '
21343 cfg.cls += ' fc-event-start';
21345 if ((i+1) == rows.length) {
21346 cfg.cls += ' fc-event-end';
21349 var ctr = _this.el.select('.fc-event-container',true).first();
21350 var cg = ctr.createChild(cfg);
21352 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21353 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21355 var r = (c.more.length) ? 1 : 0;
21356 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21357 cg.setWidth(ebox.right - sbox.x -2);
21359 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21360 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21361 cg.on('click', _this.onEventClick, _this, ev);
21372 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21373 style : 'position: absolute',
21374 unselectable : "on",
21377 cls: 'fc-event-inner',
21381 cls: 'fc-event-title',
21389 cls: 'ui-resizable-handle ui-resizable-e',
21390 html : '  '
21396 var ctr = _this.el.select('.fc-event-container',true).first();
21397 var cg = ctr.createChild(cfg);
21399 var sbox = c.select('.fc-day-content',true).first().getBox();
21400 var ebox = c.select('.fc-day-content',true).first().getBox();
21402 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21403 cg.setWidth(ebox.right - sbox.x -2);
21405 cg.on('click', _this.onMoreEventClick, _this, c.more);
21415 onEventEnter: function (e, el,event,d) {
21416 this.fireEvent('evententer', this, el, event);
21419 onEventLeave: function (e, el,event,d) {
21420 this.fireEvent('eventleave', this, el, event);
21423 onEventClick: function (e, el,event,d) {
21424 this.fireEvent('eventclick', this, el, event);
21427 onMonthChange: function () {
21431 onMoreEventClick: function(e, el, more)
21435 this.calpopover.placement = 'right';
21436 this.calpopover.setTitle('More');
21438 this.calpopover.setContent('');
21440 var ctr = this.calpopover.el.select('.popover-content', true).first();
21442 Roo.each(more, function(m){
21444 cls : 'fc-event-hori fc-event-draggable',
21447 var cg = ctr.createChild(cfg);
21449 cg.on('click', _this.onEventClick, _this, m);
21452 this.calpopover.show(el);
21457 onLoad: function ()
21459 this.calevents = [];
21462 if(this.store.getCount() > 0){
21463 this.store.data.each(function(d){
21466 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21467 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21468 time : d.data.start_time,
21469 title : d.data.title,
21470 description : d.data.description,
21471 venue : d.data.venue
21476 this.renderEvents();
21478 if(this.calevents.length && this.loadMask){
21479 this.maskEl.hide();
21483 onBeforeLoad: function()
21485 this.clearEvents();
21487 this.maskEl.show();
21501 * @class Roo.bootstrap.Popover
21502 * @extends Roo.bootstrap.Component
21504 * @children Roo.bootstrap.Component
21505 * Bootstrap Popover class
21506 * @cfg {String} html contents of the popover (or false to use children..)
21507 * @cfg {String} title of popover (or false to hide)
21508 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21509 * @cfg {String} trigger click || hover (or false to trigger manually)
21510 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21511 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21512 * - if false and it has a 'parent' then it will be automatically added to that element
21513 * - if string - Roo.get will be called
21514 * @cfg {Number} delay - delay before showing
21517 * Create a new Popover
21518 * @param {Object} config The config object
21521 Roo.bootstrap.Popover = function(config){
21522 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21528 * After the popover show
21530 * @param {Roo.bootstrap.Popover} this
21535 * After the popover hide
21537 * @param {Roo.bootstrap.Popover} this
21543 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21548 placement : 'right',
21549 trigger : 'hover', // hover
21555 can_build_overlaid : false,
21557 maskEl : false, // the mask element
21560 alignEl : false, // when show is called with an element - this get's stored.
21562 getChildContainer : function()
21564 return this.contentEl;
21567 getPopoverHeader : function()
21569 this.title = true; // flag not to hide it..
21570 this.headerEl.addClass('p-0');
21571 return this.headerEl
21575 getAutoCreate : function(){
21578 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21579 style: 'display:block',
21585 cls : 'popover-inner ',
21589 cls: 'popover-title popover-header',
21590 html : this.title === false ? '' : this.title
21593 cls : 'popover-content popover-body ' + (this.cls || ''),
21594 html : this.html || ''
21605 * @param {string} the title
21607 setTitle: function(str)
21611 this.headerEl.dom.innerHTML = str;
21616 * @param {string} the body content
21618 setContent: function(str)
21621 if (this.contentEl) {
21622 this.contentEl.dom.innerHTML = str;
21626 // as it get's added to the bottom of the page.
21627 onRender : function(ct, position)
21629 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21634 var cfg = Roo.apply({}, this.getAutoCreate());
21638 cfg.cls += ' ' + this.cls;
21641 cfg.style = this.style;
21643 //Roo.log("adding to ");
21644 this.el = Roo.get(document.body).createChild(cfg, position);
21645 // Roo.log(this.el);
21648 this.contentEl = this.el.select('.popover-content',true).first();
21649 this.headerEl = this.el.select('.popover-title',true).first();
21652 if(typeof(this.items) != 'undefined'){
21653 var items = this.items;
21656 for(var i =0;i < items.length;i++) {
21657 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21661 this.items = nitems;
21663 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21664 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21671 resizeMask : function()
21673 this.maskEl.setSize(
21674 Roo.lib.Dom.getViewWidth(true),
21675 Roo.lib.Dom.getViewHeight(true)
21679 initEvents : function()
21683 Roo.bootstrap.Popover.register(this);
21686 this.arrowEl = this.el.select('.arrow',true).first();
21687 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21688 this.el.enableDisplayMode('block');
21692 if (this.over === false && !this.parent()) {
21695 if (this.triggers === false) {
21700 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21701 var triggers = this.trigger ? this.trigger.split(' ') : [];
21702 Roo.each(triggers, function(trigger) {
21704 if (trigger == 'click') {
21705 on_el.on('click', this.toggle, this);
21706 } else if (trigger != 'manual') {
21707 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21708 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21710 on_el.on(eventIn ,this.enter, this);
21711 on_el.on(eventOut, this.leave, this);
21721 toggle : function () {
21722 this.hoverState == 'in' ? this.leave() : this.enter();
21725 enter : function () {
21727 clearTimeout(this.timeout);
21729 this.hoverState = 'in';
21731 if (!this.delay || !this.delay.show) {
21736 this.timeout = setTimeout(function () {
21737 if (_t.hoverState == 'in') {
21740 }, this.delay.show)
21743 leave : function() {
21744 clearTimeout(this.timeout);
21746 this.hoverState = 'out';
21748 if (!this.delay || !this.delay.hide) {
21753 this.timeout = setTimeout(function () {
21754 if (_t.hoverState == 'out') {
21757 }, this.delay.hide)
21761 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21762 * @param {string} (left|right|top|bottom) position
21764 show : function (on_el, placement)
21766 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21767 on_el = on_el || false; // default to false
21770 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21771 on_el = this.parent().el;
21772 } else if (this.over) {
21773 on_el = Roo.get(this.over);
21778 this.alignEl = Roo.get( on_el );
21781 this.render(document.body);
21787 if (this.title === false) {
21788 this.headerEl.hide();
21793 this.el.dom.style.display = 'block';
21796 if (this.alignEl) {
21797 this.updatePosition(this.placement, true);
21800 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21801 var es = this.el.getSize();
21802 var x = Roo.lib.Dom.getViewWidth()/2;
21803 var y = Roo.lib.Dom.getViewHeight()/2;
21804 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21809 //var arrow = this.el.select('.arrow',true).first();
21810 //arrow.set(align[2],
21812 this.el.addClass('in');
21816 this.hoverState = 'in';
21819 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21820 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21821 this.maskEl.dom.style.display = 'block';
21822 this.maskEl.addClass('show');
21824 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21826 this.fireEvent('show', this);
21830 * fire this manually after loading a grid in the table for example
21831 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21832 * @param {Boolean} try and move it if we cant get right position.
21834 updatePosition : function(placement, try_move)
21836 // allow for calling with no parameters
21837 placement = placement ? placement : this.placement;
21838 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21840 this.el.removeClass([
21841 'fade','top','bottom', 'left', 'right','in',
21842 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21844 this.el.addClass(placement + ' bs-popover-' + placement);
21846 if (!this.alignEl ) {
21850 switch (placement) {
21852 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21853 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21854 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21855 //normal display... or moved up/down.
21856 this.el.setXY(offset);
21857 var xy = this.alignEl.getAnchorXY('tr', false);
21859 this.arrowEl.setXY(xy);
21862 // continue through...
21863 return this.updatePosition('left', false);
21867 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21868 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21869 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21870 //normal display... or moved up/down.
21871 this.el.setXY(offset);
21872 var xy = this.alignEl.getAnchorXY('tl', false);
21873 xy[0]-=10;xy[1]+=5; // << fix me
21874 this.arrowEl.setXY(xy);
21878 return this.updatePosition('right', false);
21881 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21882 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21883 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21884 //normal display... or moved up/down.
21885 this.el.setXY(offset);
21886 var xy = this.alignEl.getAnchorXY('t', false);
21887 xy[1]-=10; // << fix me
21888 this.arrowEl.setXY(xy);
21892 return this.updatePosition('bottom', false);
21895 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21896 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21897 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21898 //normal display... or moved up/down.
21899 this.el.setXY(offset);
21900 var xy = this.alignEl.getAnchorXY('b', false);
21901 xy[1]+=2; // << fix me
21902 this.arrowEl.setXY(xy);
21906 return this.updatePosition('top', false);
21917 this.el.setXY([0,0]);
21918 this.el.removeClass('in');
21920 this.hoverState = null;
21921 this.maskEl.hide(); // always..
21922 this.fireEvent('hide', this);
21928 Roo.apply(Roo.bootstrap.Popover, {
21931 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21932 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21933 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21934 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21939 clickHander : false,
21943 onMouseDown : function(e)
21945 if (this.popups.length && !e.getTarget(".roo-popover")) {
21946 /// what is nothing is showing..
21955 register : function(popup)
21957 if (!Roo.bootstrap.Popover.clickHandler) {
21958 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21960 // hide other popups.
21961 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21962 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21963 this.hideAll(); //<< why?
21964 //this.popups.push(popup);
21966 hideAll : function()
21968 this.popups.forEach(function(p) {
21972 onShow : function() {
21973 Roo.bootstrap.Popover.popups.push(this);
21975 onHide : function() {
21976 Roo.bootstrap.Popover.popups.remove(this);
21982 * Card header - holder for the card header elements.
21987 * @class Roo.bootstrap.PopoverNav
21988 * @extends Roo.bootstrap.nav.Simplebar
21989 * @parent Roo.bootstrap.Popover
21990 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21991 * Bootstrap Popover header navigation class
21992 * FIXME? should this go under nav?
21996 * Create a new Popover Header Navigation
21997 * @param {Object} config The config object
22000 Roo.bootstrap.PopoverNav = function(config){
22001 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22004 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22007 container_method : 'getPopoverHeader'
22025 * @class Roo.bootstrap.Progress
22026 * @extends Roo.bootstrap.Component
22027 * @children Roo.bootstrap.ProgressBar
22028 * Bootstrap Progress class
22029 * @cfg {Boolean} striped striped of the progress bar
22030 * @cfg {Boolean} active animated of the progress bar
22034 * Create a new Progress
22035 * @param {Object} config The config object
22038 Roo.bootstrap.Progress = function(config){
22039 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22042 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22047 getAutoCreate : function(){
22055 cfg.cls += ' progress-striped';
22059 cfg.cls += ' active';
22078 * @class Roo.bootstrap.ProgressBar
22079 * @extends Roo.bootstrap.Component
22080 * Bootstrap ProgressBar class
22081 * @cfg {Number} aria_valuenow aria-value now
22082 * @cfg {Number} aria_valuemin aria-value min
22083 * @cfg {Number} aria_valuemax aria-value max
22084 * @cfg {String} label label for the progress bar
22085 * @cfg {String} panel (success | info | warning | danger )
22086 * @cfg {String} role role of the progress bar
22087 * @cfg {String} sr_only text
22091 * Create a new ProgressBar
22092 * @param {Object} config The config object
22095 Roo.bootstrap.ProgressBar = function(config){
22096 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22099 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22103 aria_valuemax : 100,
22109 getAutoCreate : function()
22114 cls: 'progress-bar',
22115 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22127 cfg.role = this.role;
22130 if(this.aria_valuenow){
22131 cfg['aria-valuenow'] = this.aria_valuenow;
22134 if(this.aria_valuemin){
22135 cfg['aria-valuemin'] = this.aria_valuemin;
22138 if(this.aria_valuemax){
22139 cfg['aria-valuemax'] = this.aria_valuemax;
22142 if(this.label && !this.sr_only){
22143 cfg.html = this.label;
22147 cfg.cls += ' progress-bar-' + this.panel;
22153 update : function(aria_valuenow)
22155 this.aria_valuenow = aria_valuenow;
22157 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22165 * @class Roo.bootstrap.TabGroup
22166 * @extends Roo.bootstrap.Column
22167 * @children Roo.bootstrap.TabPanel
22168 * Bootstrap Column class
22169 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22170 * @cfg {Boolean} carousel true to make the group behave like a carousel
22171 * @cfg {Boolean} bullets show bullets for the panels
22172 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22173 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22174 * @cfg {Boolean} showarrow (true|false) show arrow default true
22177 * Create a new TabGroup
22178 * @param {Object} config The config object
22181 Roo.bootstrap.TabGroup = function(config){
22182 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22184 this.navId = Roo.id();
22187 Roo.bootstrap.TabGroup.register(this);
22191 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22194 transition : false,
22199 slideOnTouch : false,
22202 getAutoCreate : function()
22204 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22206 cfg.cls += ' tab-content';
22208 if (this.carousel) {
22209 cfg.cls += ' carousel slide';
22212 cls : 'carousel-inner',
22216 if(this.bullets && !Roo.isTouch){
22219 cls : 'carousel-bullets',
22223 if(this.bullets_cls){
22224 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22231 cfg.cn[0].cn.push(bullets);
22234 if(this.showarrow){
22235 cfg.cn[0].cn.push({
22237 class : 'carousel-arrow',
22241 class : 'carousel-prev',
22245 class : 'fa fa-chevron-left'
22251 class : 'carousel-next',
22255 class : 'fa fa-chevron-right'
22268 initEvents: function()
22270 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22271 // this.el.on("touchstart", this.onTouchStart, this);
22274 if(this.autoslide){
22277 this.slideFn = window.setInterval(function() {
22278 _this.showPanelNext();
22282 if(this.showarrow){
22283 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22284 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22290 // onTouchStart : function(e, el, o)
22292 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22296 // this.showPanelNext();
22300 getChildContainer : function()
22302 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22306 * register a Navigation item
22307 * @param {Roo.bootstrap.nav.Item} the navitem to add
22309 register : function(item)
22311 this.tabs.push( item);
22312 item.navId = this.navId; // not really needed..
22317 getActivePanel : function()
22320 Roo.each(this.tabs, function(t) {
22330 getPanelByName : function(n)
22333 Roo.each(this.tabs, function(t) {
22334 if (t.tabId == n) {
22342 indexOfPanel : function(p)
22345 Roo.each(this.tabs, function(t,i) {
22346 if (t.tabId == p.tabId) {
22355 * show a specific panel
22356 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22357 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22359 showPanel : function (pan)
22361 if(this.transition || typeof(pan) == 'undefined'){
22362 Roo.log("waiting for the transitionend");
22366 if (typeof(pan) == 'number') {
22367 pan = this.tabs[pan];
22370 if (typeof(pan) == 'string') {
22371 pan = this.getPanelByName(pan);
22374 var cur = this.getActivePanel();
22377 Roo.log('pan or acitve pan is undefined');
22381 if (pan.tabId == this.getActivePanel().tabId) {
22385 if (false === cur.fireEvent('beforedeactivate')) {
22389 if(this.bullets > 0 && !Roo.isTouch){
22390 this.setActiveBullet(this.indexOfPanel(pan));
22393 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22395 //class="carousel-item carousel-item-next carousel-item-left"
22397 this.transition = true;
22398 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22399 var lr = dir == 'next' ? 'left' : 'right';
22400 pan.el.addClass(dir); // or prev
22401 pan.el.addClass('carousel-item-' + dir); // or prev
22402 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22403 cur.el.addClass(lr); // or right
22404 pan.el.addClass(lr);
22405 cur.el.addClass('carousel-item-' +lr); // or right
22406 pan.el.addClass('carousel-item-' +lr);
22410 cur.el.on('transitionend', function() {
22411 Roo.log("trans end?");
22413 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22414 pan.setActive(true);
22416 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22417 cur.setActive(false);
22419 _this.transition = false;
22421 }, this, { single: true } );
22426 cur.setActive(false);
22427 pan.setActive(true);
22432 showPanelNext : function()
22434 var i = this.indexOfPanel(this.getActivePanel());
22436 if (i >= this.tabs.length - 1 && !this.autoslide) {
22440 if (i >= this.tabs.length - 1 && this.autoslide) {
22444 this.showPanel(this.tabs[i+1]);
22447 showPanelPrev : function()
22449 var i = this.indexOfPanel(this.getActivePanel());
22451 if (i < 1 && !this.autoslide) {
22455 if (i < 1 && this.autoslide) {
22456 i = this.tabs.length;
22459 this.showPanel(this.tabs[i-1]);
22463 addBullet: function()
22465 if(!this.bullets || Roo.isTouch){
22468 var ctr = this.el.select('.carousel-bullets',true).first();
22469 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22470 var bullet = ctr.createChild({
22471 cls : 'bullet bullet-' + i
22472 },ctr.dom.lastChild);
22477 bullet.on('click', (function(e, el, o, ii, t){
22479 e.preventDefault();
22481 this.showPanel(ii);
22483 if(this.autoslide && this.slideFn){
22484 clearInterval(this.slideFn);
22485 this.slideFn = window.setInterval(function() {
22486 _this.showPanelNext();
22490 }).createDelegate(this, [i, bullet], true));
22495 setActiveBullet : function(i)
22501 Roo.each(this.el.select('.bullet', true).elements, function(el){
22502 el.removeClass('selected');
22505 var bullet = this.el.select('.bullet-' + i, true).first();
22511 bullet.addClass('selected');
22522 Roo.apply(Roo.bootstrap.TabGroup, {
22526 * register a Navigation Group
22527 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22529 register : function(navgrp)
22531 this.groups[navgrp.navId] = navgrp;
22535 * fetch a Navigation Group based on the navigation ID
22536 * if one does not exist , it will get created.
22537 * @param {string} the navgroup to add
22538 * @returns {Roo.bootstrap.nav.Group} the navgroup
22540 get: function(navId) {
22541 if (typeof(this.groups[navId]) == 'undefined') {
22542 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22544 return this.groups[navId] ;
22559 * @class Roo.bootstrap.TabPanel
22560 * @extends Roo.bootstrap.Component
22561 * @children Roo.bootstrap.Component
22562 * Bootstrap TabPanel class
22563 * @cfg {Boolean} active panel active
22564 * @cfg {String} html panel content
22565 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22566 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22567 * @cfg {String} href click to link..
22568 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22572 * Create a new TabPanel
22573 * @param {Object} config The config object
22576 Roo.bootstrap.TabPanel = function(config){
22577 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22581 * Fires when the active status changes
22582 * @param {Roo.bootstrap.TabPanel} this
22583 * @param {Boolean} state the new state
22588 * @event beforedeactivate
22589 * Fires before a tab is de-activated - can be used to do validation on a form.
22590 * @param {Roo.bootstrap.TabPanel} this
22591 * @return {Boolean} false if there is an error
22594 'beforedeactivate': true
22597 this.tabId = this.tabId || Roo.id();
22601 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22608 touchSlide : false,
22609 getAutoCreate : function(){
22614 // item is needed for carousel - not sure if it has any effect otherwise
22615 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22616 html: this.html || ''
22620 cfg.cls += ' active';
22624 cfg.tabId = this.tabId;
22632 initEvents: function()
22634 var p = this.parent();
22636 this.navId = this.navId || p.navId;
22638 if (typeof(this.navId) != 'undefined') {
22639 // not really needed.. but just in case.. parent should be a NavGroup.
22640 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22644 var i = tg.tabs.length - 1;
22646 if(this.active && tg.bullets > 0 && i < tg.bullets){
22647 tg.setActiveBullet(i);
22651 this.el.on('click', this.onClick, this);
22653 if(Roo.isTouch && this.touchSlide){
22654 this.el.on("touchstart", this.onTouchStart, this);
22655 this.el.on("touchmove", this.onTouchMove, this);
22656 this.el.on("touchend", this.onTouchEnd, this);
22661 onRender : function(ct, position)
22663 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22666 setActive : function(state)
22668 Roo.log("panel - set active " + this.tabId + "=" + state);
22670 this.active = state;
22672 this.el.removeClass('active');
22674 } else if (!this.el.hasClass('active')) {
22675 this.el.addClass('active');
22678 this.fireEvent('changed', this, state);
22681 onClick : function(e)
22683 e.preventDefault();
22685 if(!this.href.length){
22689 window.location.href = this.href;
22698 onTouchStart : function(e)
22700 this.swiping = false;
22702 this.startX = e.browserEvent.touches[0].clientX;
22703 this.startY = e.browserEvent.touches[0].clientY;
22706 onTouchMove : function(e)
22708 this.swiping = true;
22710 this.endX = e.browserEvent.touches[0].clientX;
22711 this.endY = e.browserEvent.touches[0].clientY;
22714 onTouchEnd : function(e)
22721 var tabGroup = this.parent();
22723 if(this.endX > this.startX){ // swiping right
22724 tabGroup.showPanelPrev();
22728 if(this.startX > this.endX){ // swiping left
22729 tabGroup.showPanelNext();
22748 * @class Roo.bootstrap.DateField
22749 * @extends Roo.bootstrap.Input
22750 * Bootstrap DateField class
22751 * @cfg {Number} weekStart default 0
22752 * @cfg {String} viewMode default empty, (months|years)
22753 * @cfg {String} minViewMode default empty, (months|years)
22754 * @cfg {Number} startDate default -Infinity
22755 * @cfg {Number} endDate default Infinity
22756 * @cfg {Boolean} todayHighlight default false
22757 * @cfg {Boolean} todayBtn default false
22758 * @cfg {Boolean} calendarWeeks default false
22759 * @cfg {Object} daysOfWeekDisabled default empty
22760 * @cfg {Boolean} singleMode default false (true | false)
22762 * @cfg {Boolean} keyboardNavigation default true
22763 * @cfg {String} language default en
22766 * Create a new DateField
22767 * @param {Object} config The config object
22770 Roo.bootstrap.DateField = function(config){
22771 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22775 * Fires when this field show.
22776 * @param {Roo.bootstrap.DateField} this
22777 * @param {Mixed} date The date value
22782 * Fires when this field hide.
22783 * @param {Roo.bootstrap.DateField} this
22784 * @param {Mixed} date The date value
22789 * Fires when select a date.
22790 * @param {Roo.bootstrap.DateField} this
22791 * @param {Mixed} date The date value
22795 * @event beforeselect
22796 * Fires when before select a date.
22797 * @param {Roo.bootstrap.DateField} this
22798 * @param {Mixed} date The date value
22800 beforeselect : true
22804 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22807 * @cfg {String} format
22808 * The default date format string which can be overriden for localization support. The format must be
22809 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22813 * @cfg {String} altFormats
22814 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22815 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22817 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22825 todayHighlight : false,
22831 keyboardNavigation: true,
22833 calendarWeeks: false,
22835 startDate: -Infinity,
22839 daysOfWeekDisabled: [],
22843 singleMode : false,
22845 UTCDate: function()
22847 return new Date(Date.UTC.apply(Date, arguments));
22850 UTCToday: function()
22852 var today = new Date();
22853 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22856 getDate: function() {
22857 var d = this.getUTCDate();
22858 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22861 getUTCDate: function() {
22865 setDate: function(d) {
22866 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22869 setUTCDate: function(d) {
22871 this.setValue(this.formatDate(this.date));
22874 onRender: function(ct, position)
22877 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22879 this.language = this.language || 'en';
22880 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22881 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22883 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22884 this.format = this.format || 'm/d/y';
22885 this.isInline = false;
22886 this.isInput = true;
22887 this.component = this.el.select('.add-on', true).first() || false;
22888 this.component = (this.component && this.component.length === 0) ? false : this.component;
22889 this.hasInput = this.component && this.inputEl().length;
22891 if (typeof(this.minViewMode === 'string')) {
22892 switch (this.minViewMode) {
22894 this.minViewMode = 1;
22897 this.minViewMode = 2;
22900 this.minViewMode = 0;
22905 if (typeof(this.viewMode === 'string')) {
22906 switch (this.viewMode) {
22919 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22921 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22923 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22925 this.picker().on('mousedown', this.onMousedown, this);
22926 this.picker().on('click', this.onClick, this);
22928 this.picker().addClass('datepicker-dropdown');
22930 this.startViewMode = this.viewMode;
22932 if(this.singleMode){
22933 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22934 v.setVisibilityMode(Roo.Element.DISPLAY);
22938 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22939 v.setStyle('width', '189px');
22943 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22944 if(!this.calendarWeeks){
22949 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22950 v.attr('colspan', function(i, val){
22951 return parseInt(val) + 1;
22956 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22958 this.setStartDate(this.startDate);
22959 this.setEndDate(this.endDate);
22961 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22968 if(this.isInline) {
22973 picker : function()
22975 return this.pickerEl;
22976 // return this.el.select('.datepicker', true).first();
22979 fillDow: function()
22981 var dowCnt = this.weekStart;
22990 if(this.calendarWeeks){
22998 while (dowCnt < this.weekStart + 7) {
23002 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23006 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23009 fillMonths: function()
23012 var months = this.picker().select('>.datepicker-months td', true).first();
23014 months.dom.innerHTML = '';
23020 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
23023 months.createChild(month);
23030 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;
23032 if (this.date < this.startDate) {
23033 this.viewDate = new Date(this.startDate);
23034 } else if (this.date > this.endDate) {
23035 this.viewDate = new Date(this.endDate);
23037 this.viewDate = new Date(this.date);
23045 var d = new Date(this.viewDate),
23046 year = d.getUTCFullYear(),
23047 month = d.getUTCMonth(),
23048 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23049 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23050 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23051 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23052 currentDate = this.date && this.date.valueOf(),
23053 today = this.UTCToday();
23055 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
23057 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
23059 // this.picker.select('>tfoot th.today').
23060 // .text(dates[this.language].today)
23061 // .toggle(this.todayBtn !== false);
23063 this.updateNavArrows();
23066 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23068 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23070 prevMonth.setUTCDate(day);
23072 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23074 var nextMonth = new Date(prevMonth);
23076 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23078 nextMonth = nextMonth.valueOf();
23080 var fillMonths = false;
23082 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23084 while(prevMonth.valueOf() <= nextMonth) {
23087 if (prevMonth.getUTCDay() === this.weekStart) {
23089 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23097 if(this.calendarWeeks){
23098 // ISO 8601: First week contains first thursday.
23099 // ISO also states week starts on Monday, but we can be more abstract here.
23101 // Start of current week: based on weekstart/current date
23102 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23103 // Thursday of this week
23104 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23105 // First Thursday of year, year from thursday
23106 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23107 // Calendar week: ms between thursdays, div ms per day, div 7 days
23108 calWeek = (th - yth) / 864e5 / 7 + 1;
23110 fillMonths.cn.push({
23118 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23120 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23123 if (this.todayHighlight &&
23124 prevMonth.getUTCFullYear() == today.getFullYear() &&
23125 prevMonth.getUTCMonth() == today.getMonth() &&
23126 prevMonth.getUTCDate() == today.getDate()) {
23127 clsName += ' today';
23130 if (currentDate && prevMonth.valueOf() === currentDate) {
23131 clsName += ' active';
23134 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23135 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23136 clsName += ' disabled';
23139 fillMonths.cn.push({
23141 cls: 'day ' + clsName,
23142 html: prevMonth.getDate()
23145 prevMonth.setDate(prevMonth.getDate()+1);
23148 var currentYear = this.date && this.date.getUTCFullYear();
23149 var currentMonth = this.date && this.date.getUTCMonth();
23151 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23153 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23154 v.removeClass('active');
23156 if(currentYear === year && k === currentMonth){
23157 v.addClass('active');
23160 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23161 v.addClass('disabled');
23167 year = parseInt(year/10, 10) * 10;
23169 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23171 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23174 for (var i = -1; i < 11; i++) {
23175 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23177 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23185 showMode: function(dir)
23188 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23191 Roo.each(this.picker().select('>div',true).elements, function(v){
23192 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23195 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
23200 if(this.isInline) {
23204 this.picker().removeClass(['bottom', 'top']);
23206 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23208 * place to the top of element!
23212 this.picker().addClass('top');
23213 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23218 this.picker().addClass('bottom');
23220 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23223 parseDate : function(value)
23225 if(!value || value instanceof Date){
23228 var v = Date.parseDate(value, this.format);
23229 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23230 v = Date.parseDate(value, 'Y-m-d');
23232 if(!v && this.altFormats){
23233 if(!this.altFormatsArray){
23234 this.altFormatsArray = this.altFormats.split("|");
23236 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23237 v = Date.parseDate(value, this.altFormatsArray[i]);
23243 formatDate : function(date, fmt)
23245 return (!date || !(date instanceof Date)) ?
23246 date : date.dateFormat(fmt || this.format);
23249 onFocus : function()
23251 Roo.bootstrap.DateField.superclass.onFocus.call(this);
23255 onBlur : function()
23257 Roo.bootstrap.DateField.superclass.onBlur.call(this);
23259 var d = this.inputEl().getValue();
23266 showPopup : function()
23268 this.picker().show();
23272 this.fireEvent('showpopup', this, this.date);
23275 hidePopup : function()
23277 if(this.isInline) {
23280 this.picker().hide();
23281 this.viewMode = this.startViewMode;
23284 this.fireEvent('hidepopup', this, this.date);
23288 onMousedown: function(e)
23290 e.stopPropagation();
23291 e.preventDefault();
23296 Roo.bootstrap.DateField.superclass.keyup.call(this);
23300 setValue: function(v)
23302 if(this.fireEvent('beforeselect', this, v) !== false){
23303 var d = new Date(this.parseDate(v) ).clearTime();
23305 if(isNaN(d.getTime())){
23306 this.date = this.viewDate = '';
23307 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23311 v = this.formatDate(d);
23313 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
23315 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23319 this.fireEvent('select', this, this.date);
23323 getValue: function()
23325 return this.formatDate(this.date);
23328 fireKey: function(e)
23330 if (!this.picker().isVisible()){
23331 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23337 var dateChanged = false,
23339 newDate, newViewDate;
23344 e.preventDefault();
23348 if (!this.keyboardNavigation) {
23351 dir = e.keyCode == 37 ? -1 : 1;
23354 newDate = this.moveYear(this.date, dir);
23355 newViewDate = this.moveYear(this.viewDate, dir);
23356 } else if (e.shiftKey){
23357 newDate = this.moveMonth(this.date, dir);
23358 newViewDate = this.moveMonth(this.viewDate, dir);
23360 newDate = new Date(this.date);
23361 newDate.setUTCDate(this.date.getUTCDate() + dir);
23362 newViewDate = new Date(this.viewDate);
23363 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23365 if (this.dateWithinRange(newDate)){
23366 this.date = newDate;
23367 this.viewDate = newViewDate;
23368 this.setValue(this.formatDate(this.date));
23370 e.preventDefault();
23371 dateChanged = true;
23376 if (!this.keyboardNavigation) {
23379 dir = e.keyCode == 38 ? -1 : 1;
23381 newDate = this.moveYear(this.date, dir);
23382 newViewDate = this.moveYear(this.viewDate, dir);
23383 } else if (e.shiftKey){
23384 newDate = this.moveMonth(this.date, dir);
23385 newViewDate = this.moveMonth(this.viewDate, dir);
23387 newDate = new Date(this.date);
23388 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23389 newViewDate = new Date(this.viewDate);
23390 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23392 if (this.dateWithinRange(newDate)){
23393 this.date = newDate;
23394 this.viewDate = newViewDate;
23395 this.setValue(this.formatDate(this.date));
23397 e.preventDefault();
23398 dateChanged = true;
23402 this.setValue(this.formatDate(this.date));
23404 e.preventDefault();
23407 this.setValue(this.formatDate(this.date));
23421 onClick: function(e)
23423 e.stopPropagation();
23424 e.preventDefault();
23426 var target = e.getTarget();
23428 if(target.nodeName.toLowerCase() === 'i'){
23429 target = Roo.get(target).dom.parentNode;
23432 var nodeName = target.nodeName;
23433 var className = target.className;
23434 var html = target.innerHTML;
23435 //Roo.log(nodeName);
23437 switch(nodeName.toLowerCase()) {
23439 switch(className) {
23445 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23446 switch(this.viewMode){
23448 this.viewDate = this.moveMonth(this.viewDate, dir);
23452 this.viewDate = this.moveYear(this.viewDate, dir);
23458 var date = new Date();
23459 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23461 this.setValue(this.formatDate(this.date));
23468 if (className.indexOf('disabled') < 0) {
23469 if (!this.viewDate) {
23470 this.viewDate = new Date();
23472 this.viewDate.setUTCDate(1);
23473 if (className.indexOf('month') > -1) {
23474 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23476 var year = parseInt(html, 10) || 0;
23477 this.viewDate.setUTCFullYear(year);
23481 if(this.singleMode){
23482 this.setValue(this.formatDate(this.viewDate));
23493 //Roo.log(className);
23494 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23495 var day = parseInt(html, 10) || 1;
23496 var year = (this.viewDate || new Date()).getUTCFullYear(),
23497 month = (this.viewDate || new Date()).getUTCMonth();
23499 if (className.indexOf('old') > -1) {
23506 } else if (className.indexOf('new') > -1) {
23514 //Roo.log([year,month,day]);
23515 this.date = this.UTCDate(year, month, day,0,0,0,0);
23516 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23518 //Roo.log(this.formatDate(this.date));
23519 this.setValue(this.formatDate(this.date));
23526 setStartDate: function(startDate)
23528 this.startDate = startDate || -Infinity;
23529 if (this.startDate !== -Infinity) {
23530 this.startDate = this.parseDate(this.startDate);
23533 this.updateNavArrows();
23536 setEndDate: function(endDate)
23538 this.endDate = endDate || Infinity;
23539 if (this.endDate !== Infinity) {
23540 this.endDate = this.parseDate(this.endDate);
23543 this.updateNavArrows();
23546 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23548 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23549 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23550 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23552 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23553 return parseInt(d, 10);
23556 this.updateNavArrows();
23559 updateNavArrows: function()
23561 if(this.singleMode){
23565 var d = new Date(this.viewDate),
23566 year = d.getUTCFullYear(),
23567 month = d.getUTCMonth();
23569 Roo.each(this.picker().select('.prev', true).elements, function(v){
23571 switch (this.viewMode) {
23574 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23580 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23587 Roo.each(this.picker().select('.next', true).elements, function(v){
23589 switch (this.viewMode) {
23592 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23598 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23606 moveMonth: function(date, dir)
23611 var new_date = new Date(date.valueOf()),
23612 day = new_date.getUTCDate(),
23613 month = new_date.getUTCMonth(),
23614 mag = Math.abs(dir),
23616 dir = dir > 0 ? 1 : -1;
23619 // If going back one month, make sure month is not current month
23620 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23622 return new_date.getUTCMonth() == month;
23624 // If going forward one month, make sure month is as expected
23625 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23627 return new_date.getUTCMonth() != new_month;
23629 new_month = month + dir;
23630 new_date.setUTCMonth(new_month);
23631 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23632 if (new_month < 0 || new_month > 11) {
23633 new_month = (new_month + 12) % 12;
23636 // For magnitudes >1, move one month at a time...
23637 for (var i=0; i<mag; i++) {
23638 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23639 new_date = this.moveMonth(new_date, dir);
23641 // ...then reset the day, keeping it in the new month
23642 new_month = new_date.getUTCMonth();
23643 new_date.setUTCDate(day);
23645 return new_month != new_date.getUTCMonth();
23648 // Common date-resetting loop -- if date is beyond end of month, make it
23651 new_date.setUTCDate(--day);
23652 new_date.setUTCMonth(new_month);
23657 moveYear: function(date, dir)
23659 return this.moveMonth(date, dir*12);
23662 dateWithinRange: function(date)
23664 return date >= this.startDate && date <= this.endDate;
23670 this.picker().remove();
23673 validateValue : function(value)
23675 if(this.getVisibilityEl().hasClass('hidden')){
23679 if(value.length < 1) {
23680 if(this.allowBlank){
23686 if(value.length < this.minLength){
23689 if(value.length > this.maxLength){
23693 var vt = Roo.form.VTypes;
23694 if(!vt[this.vtype](value, this)){
23698 if(typeof this.validator == "function"){
23699 var msg = this.validator(value);
23705 if(this.regex && !this.regex.test(value)){
23709 if(typeof(this.parseDate(value)) == 'undefined'){
23713 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23717 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23727 this.date = this.viewDate = '';
23729 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23734 Roo.apply(Roo.bootstrap.DateField, {
23745 html: '<i class="fa fa-arrow-left"/>'
23755 html: '<i class="fa fa-arrow-right"/>'
23797 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23798 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23799 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23800 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23801 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23814 navFnc: 'FullYear',
23819 navFnc: 'FullYear',
23824 Roo.apply(Roo.bootstrap.DateField, {
23828 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23832 cls: 'datepicker-days',
23836 cls: 'table-condensed',
23838 Roo.bootstrap.DateField.head,
23842 Roo.bootstrap.DateField.footer
23849 cls: 'datepicker-months',
23853 cls: 'table-condensed',
23855 Roo.bootstrap.DateField.head,
23856 Roo.bootstrap.DateField.content,
23857 Roo.bootstrap.DateField.footer
23864 cls: 'datepicker-years',
23868 cls: 'table-condensed',
23870 Roo.bootstrap.DateField.head,
23871 Roo.bootstrap.DateField.content,
23872 Roo.bootstrap.DateField.footer
23891 * @class Roo.bootstrap.TimeField
23892 * @extends Roo.bootstrap.Input
23893 * Bootstrap DateField class
23897 * Create a new TimeField
23898 * @param {Object} config The config object
23901 Roo.bootstrap.TimeField = function(config){
23902 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23906 * Fires when this field show.
23907 * @param {Roo.bootstrap.DateField} thisthis
23908 * @param {Mixed} date The date value
23913 * Fires when this field hide.
23914 * @param {Roo.bootstrap.DateField} this
23915 * @param {Mixed} date The date value
23920 * Fires when select a date.
23921 * @param {Roo.bootstrap.DateField} this
23922 * @param {Mixed} date The date value
23928 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23931 * @cfg {String} format
23932 * The default time format string which can be overriden for localization support. The format must be
23933 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23937 getAutoCreate : function()
23939 this.after = '<i class="fa far fa-clock"></i>';
23940 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23944 onRender: function(ct, position)
23947 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23949 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23951 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23953 this.pop = this.picker().select('>.datepicker-time',true).first();
23954 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23956 this.picker().on('mousedown', this.onMousedown, this);
23957 this.picker().on('click', this.onClick, this);
23959 this.picker().addClass('datepicker-dropdown');
23964 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23965 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23966 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23967 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23968 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23969 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23973 fireKey: function(e){
23974 if (!this.picker().isVisible()){
23975 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23981 e.preventDefault();
23989 this.onTogglePeriod();
23992 this.onIncrementMinutes();
23995 this.onDecrementMinutes();
24004 onClick: function(e) {
24005 e.stopPropagation();
24006 e.preventDefault();
24009 picker : function()
24011 return this.pickerEl;
24014 fillTime: function()
24016 var time = this.pop.select('tbody', true).first();
24018 time.dom.innerHTML = '';
24033 cls: 'hours-up fa fas fa-chevron-up'
24053 cls: 'minutes-up fa fas fa-chevron-up'
24074 cls: 'timepicker-hour',
24089 cls: 'timepicker-minute',
24104 cls: 'btn btn-primary period',
24126 cls: 'hours-down fa fas fa-chevron-down'
24146 cls: 'minutes-down fa fas fa-chevron-down'
24164 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24171 var hours = this.time.getHours();
24172 var minutes = this.time.getMinutes();
24185 hours = hours - 12;
24189 hours = '0' + hours;
24193 minutes = '0' + minutes;
24196 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24197 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24198 this.pop.select('button', true).first().dom.innerHTML = period;
24204 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24206 var cls = ['bottom'];
24208 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24215 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24219 //this.picker().setXY(20000,20000);
24220 this.picker().addClass(cls.join('-'));
24224 Roo.each(cls, function(c){
24229 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24230 //_this.picker().setTop(_this.inputEl().getHeight());
24234 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24236 //_this.picker().setTop(0 - _this.picker().getHeight());
24241 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24245 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24253 onFocus : function()
24255 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
24259 onBlur : function()
24261 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
24267 this.picker().show();
24272 this.fireEvent('show', this, this.date);
24277 this.picker().hide();
24280 this.fireEvent('hide', this, this.date);
24283 setTime : function()
24286 this.setValue(this.time.format(this.format));
24288 this.fireEvent('select', this, this.date);
24293 onMousedown: function(e){
24294 e.stopPropagation();
24295 e.preventDefault();
24298 onIncrementHours: function()
24300 Roo.log('onIncrementHours');
24301 this.time = this.time.add(Date.HOUR, 1);
24306 onDecrementHours: function()
24308 Roo.log('onDecrementHours');
24309 this.time = this.time.add(Date.HOUR, -1);
24313 onIncrementMinutes: function()
24315 Roo.log('onIncrementMinutes');
24316 this.time = this.time.add(Date.MINUTE, 1);
24320 onDecrementMinutes: function()
24322 Roo.log('onDecrementMinutes');
24323 this.time = this.time.add(Date.MINUTE, -1);
24327 onTogglePeriod: function()
24329 Roo.log('onTogglePeriod');
24330 this.time = this.time.add(Date.HOUR, 12);
24338 Roo.apply(Roo.bootstrap.TimeField, {
24342 cls: 'datepicker dropdown-menu',
24346 cls: 'datepicker-time',
24350 cls: 'table-condensed',
24379 cls: 'btn btn-info ok',
24407 * @class Roo.bootstrap.MonthField
24408 * @extends Roo.bootstrap.Input
24409 * Bootstrap MonthField class
24411 * @cfg {String} language default en
24414 * Create a new MonthField
24415 * @param {Object} config The config object
24418 Roo.bootstrap.MonthField = function(config){
24419 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
24424 * Fires when this field show.
24425 * @param {Roo.bootstrap.MonthField} this
24426 * @param {Mixed} date The date value
24431 * Fires when this field hide.
24432 * @param {Roo.bootstrap.MonthField} this
24433 * @param {Mixed} date The date value
24438 * Fires when select a date.
24439 * @param {Roo.bootstrap.MonthField} this
24440 * @param {String} oldvalue The old value
24441 * @param {String} newvalue The new value
24447 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24449 onRender: function(ct, position)
24452 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24454 this.language = this.language || 'en';
24455 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24456 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24458 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24459 this.isInline = false;
24460 this.isInput = true;
24461 this.component = this.el.select('.add-on', true).first() || false;
24462 this.component = (this.component && this.component.length === 0) ? false : this.component;
24463 this.hasInput = this.component && this.inputEL().length;
24465 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24467 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24469 this.picker().on('mousedown', this.onMousedown, this);
24470 this.picker().on('click', this.onClick, this);
24472 this.picker().addClass('datepicker-dropdown');
24474 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24475 v.setStyle('width', '189px');
24482 if(this.isInline) {
24488 setValue: function(v, suppressEvent)
24490 var o = this.getValue();
24492 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24496 if(suppressEvent !== true){
24497 this.fireEvent('select', this, o, v);
24502 getValue: function()
24507 onClick: function(e)
24509 e.stopPropagation();
24510 e.preventDefault();
24512 var target = e.getTarget();
24514 if(target.nodeName.toLowerCase() === 'i'){
24515 target = Roo.get(target).dom.parentNode;
24518 var nodeName = target.nodeName;
24519 var className = target.className;
24520 var html = target.innerHTML;
24522 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24526 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24528 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24534 picker : function()
24536 return this.pickerEl;
24539 fillMonths: function()
24542 var months = this.picker().select('>.datepicker-months td', true).first();
24544 months.dom.innerHTML = '';
24550 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24553 months.createChild(month);
24562 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24563 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24566 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24567 e.removeClass('active');
24569 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24570 e.addClass('active');
24577 if(this.isInline) {
24581 this.picker().removeClass(['bottom', 'top']);
24583 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24585 * place to the top of element!
24589 this.picker().addClass('top');
24590 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24595 this.picker().addClass('bottom');
24597 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24600 onFocus : function()
24602 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24606 onBlur : function()
24608 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24610 var d = this.inputEl().getValue();
24619 this.picker().show();
24620 this.picker().select('>.datepicker-months', true).first().show();
24624 this.fireEvent('show', this, this.date);
24629 if(this.isInline) {
24632 this.picker().hide();
24633 this.fireEvent('hide', this, this.date);
24637 onMousedown: function(e)
24639 e.stopPropagation();
24640 e.preventDefault();
24645 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24649 fireKey: function(e)
24651 if (!this.picker().isVisible()){
24652 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24663 e.preventDefault();
24667 dir = e.keyCode == 37 ? -1 : 1;
24669 this.vIndex = this.vIndex + dir;
24671 if(this.vIndex < 0){
24675 if(this.vIndex > 11){
24679 if(isNaN(this.vIndex)){
24683 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24689 dir = e.keyCode == 38 ? -1 : 1;
24691 this.vIndex = this.vIndex + dir * 4;
24693 if(this.vIndex < 0){
24697 if(this.vIndex > 11){
24701 if(isNaN(this.vIndex)){
24705 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24710 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24711 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24715 e.preventDefault();
24718 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24719 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24735 this.picker().remove();
24740 Roo.apply(Roo.bootstrap.MonthField, {
24759 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24760 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24765 Roo.apply(Roo.bootstrap.MonthField, {
24769 cls: 'datepicker dropdown-menu roo-dynamic',
24773 cls: 'datepicker-months',
24777 cls: 'table-condensed',
24779 Roo.bootstrap.DateField.content
24799 * @class Roo.bootstrap.CheckBox
24800 * @extends Roo.bootstrap.Input
24801 * Bootstrap CheckBox class
24803 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24804 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24805 * @cfg {String} boxLabel The text that appears beside the checkbox
24806 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24807 * @cfg {Boolean} checked initnal the element
24808 * @cfg {Boolean} inline inline the element (default false)
24809 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24810 * @cfg {String} tooltip label tooltip
24813 * Create a new CheckBox
24814 * @param {Object} config The config object
24817 Roo.bootstrap.CheckBox = function(config){
24818 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24823 * Fires when the element is checked or unchecked.
24824 * @param {Roo.bootstrap.CheckBox} this This input
24825 * @param {Boolean} checked The new checked value
24830 * Fires when the element is click.
24831 * @param {Roo.bootstrap.CheckBox} this This input
24838 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24840 inputType: 'checkbox',
24849 // checkbox success does not make any sense really..
24854 getAutoCreate : function()
24856 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24862 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24865 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24871 type : this.inputType,
24872 value : this.inputValue,
24873 cls : 'roo-' + this.inputType, //'form-box',
24874 placeholder : this.placeholder || ''
24878 if(this.inputType != 'radio'){
24882 cls : 'roo-hidden-value',
24883 value : this.checked ? this.inputValue : this.valueOff
24888 if (this.weight) { // Validity check?
24889 cfg.cls += " " + this.inputType + "-" + this.weight;
24892 if (this.disabled) {
24893 input.disabled=true;
24897 input.checked = this.checked;
24902 input.name = this.name;
24904 if(this.inputType != 'radio'){
24905 hidden.name = this.name;
24906 input.name = '_hidden_' + this.name;
24911 input.cls += ' input-' + this.size;
24916 ['xs','sm','md','lg'].map(function(size){
24917 if (settings[size]) {
24918 cfg.cls += ' col-' + size + '-' + settings[size];
24922 var inputblock = input;
24924 if (this.before || this.after) {
24927 cls : 'input-group',
24932 inputblock.cn.push({
24934 cls : 'input-group-addon',
24939 inputblock.cn.push(input);
24941 if(this.inputType != 'radio'){
24942 inputblock.cn.push(hidden);
24946 inputblock.cn.push({
24948 cls : 'input-group-addon',
24954 var boxLabelCfg = false;
24960 //'for': id, // box label is handled by onclick - so no for...
24962 html: this.boxLabel
24965 boxLabelCfg.tooltip = this.tooltip;
24971 if (align ==='left' && this.fieldLabel.length) {
24972 // Roo.log("left and has label");
24977 cls : 'control-label',
24978 html : this.fieldLabel
24989 cfg.cn[1].cn.push(boxLabelCfg);
24992 if(this.labelWidth > 12){
24993 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24996 if(this.labelWidth < 13 && this.labelmd == 0){
24997 this.labelmd = this.labelWidth;
25000 if(this.labellg > 0){
25001 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25002 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25005 if(this.labelmd > 0){
25006 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25007 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25010 if(this.labelsm > 0){
25011 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25012 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25015 if(this.labelxs > 0){
25016 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25017 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25020 } else if ( this.fieldLabel.length) {
25021 // Roo.log(" label");
25025 tag: this.boxLabel ? 'span' : 'label',
25027 cls: 'control-label box-input-label',
25028 //cls : 'input-group-addon',
25029 html : this.fieldLabel
25036 cfg.cn.push(boxLabelCfg);
25041 // Roo.log(" no label && no align");
25042 cfg.cn = [ inputblock ] ;
25044 cfg.cn.push(boxLabelCfg);
25052 if(this.inputType != 'radio'){
25053 cfg.cn.push(hidden);
25061 * return the real input element.
25063 inputEl: function ()
25065 return this.el.select('input.roo-' + this.inputType,true).first();
25067 hiddenEl: function ()
25069 return this.el.select('input.roo-hidden-value',true).first();
25072 labelEl: function()
25074 return this.el.select('label.control-label',true).first();
25076 /* depricated... */
25080 return this.labelEl();
25083 boxLabelEl: function()
25085 return this.el.select('label.box-label',true).first();
25088 initEvents : function()
25090 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
25092 this.inputEl().on('click', this.onClick, this);
25094 if (this.boxLabel) {
25095 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25098 this.startValue = this.getValue();
25101 Roo.bootstrap.CheckBox.register(this);
25105 onClick : function(e)
25107 if(this.fireEvent('click', this, e) !== false){
25108 this.setChecked(!this.checked);
25113 setChecked : function(state,suppressEvent)
25115 this.startValue = this.getValue();
25117 if(this.inputType == 'radio'){
25119 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25120 e.dom.checked = false;
25123 this.inputEl().dom.checked = true;
25125 this.inputEl().dom.value = this.inputValue;
25127 if(suppressEvent !== true){
25128 this.fireEvent('check', this, true);
25136 this.checked = state;
25138 this.inputEl().dom.checked = state;
25141 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25143 if(suppressEvent !== true){
25144 this.fireEvent('check', this, state);
25150 getValue : function()
25152 if(this.inputType == 'radio'){
25153 return this.getGroupValue();
25156 return this.hiddenEl().dom.value;
25160 getGroupValue : function()
25162 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25166 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25169 setValue : function(v,suppressEvent)
25171 if(this.inputType == 'radio'){
25172 this.setGroupValue(v, suppressEvent);
25176 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25181 setGroupValue : function(v, suppressEvent)
25183 this.startValue = this.getValue();
25185 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25186 e.dom.checked = false;
25188 if(e.dom.value == v){
25189 e.dom.checked = true;
25193 if(suppressEvent !== true){
25194 this.fireEvent('check', this, true);
25202 validate : function()
25204 if(this.getVisibilityEl().hasClass('hidden')){
25210 (this.inputType == 'radio' && this.validateRadio()) ||
25211 (this.inputType == 'checkbox' && this.validateCheckbox())
25217 this.markInvalid();
25221 validateRadio : function()
25223 if(this.getVisibilityEl().hasClass('hidden')){
25227 if(this.allowBlank){
25233 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25234 if(!e.dom.checked){
25246 validateCheckbox : function()
25249 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25250 //return (this.getValue() == this.inputValue) ? true : false;
25253 var group = Roo.bootstrap.CheckBox.get(this.groupId);
25261 for(var i in group){
25262 if(group[i].el.isVisible(true)){
25270 for(var i in group){
25275 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25282 * Mark this field as valid
25284 markValid : function()
25288 this.fireEvent('valid', this);
25290 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25293 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
25300 if(this.inputType == 'radio'){
25301 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25302 var fg = e.findParent('.form-group', false, true);
25303 if (Roo.bootstrap.version == 3) {
25304 fg.removeClass([_this.invalidClass, _this.validClass]);
25305 fg.addClass(_this.validClass);
25307 fg.removeClass(['is-valid', 'is-invalid']);
25308 fg.addClass('is-valid');
25316 var fg = this.el.findParent('.form-group', false, true);
25317 if (Roo.bootstrap.version == 3) {
25318 fg.removeClass([this.invalidClass, this.validClass]);
25319 fg.addClass(this.validClass);
25321 fg.removeClass(['is-valid', 'is-invalid']);
25322 fg.addClass('is-valid');
25327 var group = Roo.bootstrap.CheckBox.get(this.groupId);
25333 for(var i in group){
25334 var fg = group[i].el.findParent('.form-group', false, true);
25335 if (Roo.bootstrap.version == 3) {
25336 fg.removeClass([this.invalidClass, this.validClass]);
25337 fg.addClass(this.validClass);
25339 fg.removeClass(['is-valid', 'is-invalid']);
25340 fg.addClass('is-valid');
25346 * Mark this field as invalid
25347 * @param {String} msg The validation message
25349 markInvalid : function(msg)
25351 if(this.allowBlank){
25357 this.fireEvent('invalid', this, msg);
25359 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25362 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
25366 label.markInvalid();
25369 if(this.inputType == 'radio'){
25371 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25372 var fg = e.findParent('.form-group', false, true);
25373 if (Roo.bootstrap.version == 3) {
25374 fg.removeClass([_this.invalidClass, _this.validClass]);
25375 fg.addClass(_this.invalidClass);
25377 fg.removeClass(['is-invalid', 'is-valid']);
25378 fg.addClass('is-invalid');
25386 var fg = this.el.findParent('.form-group', false, true);
25387 if (Roo.bootstrap.version == 3) {
25388 fg.removeClass([_this.invalidClass, _this.validClass]);
25389 fg.addClass(_this.invalidClass);
25391 fg.removeClass(['is-invalid', 'is-valid']);
25392 fg.addClass('is-invalid');
25397 var group = Roo.bootstrap.CheckBox.get(this.groupId);
25403 for(var i in group){
25404 var fg = group[i].el.findParent('.form-group', false, true);
25405 if (Roo.bootstrap.version == 3) {
25406 fg.removeClass([_this.invalidClass, _this.validClass]);
25407 fg.addClass(_this.invalidClass);
25409 fg.removeClass(['is-invalid', 'is-valid']);
25410 fg.addClass('is-invalid');
25416 clearInvalid : function()
25418 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
25420 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25422 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25424 if (label && label.iconEl) {
25425 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25426 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25430 disable : function()
25432 if(this.inputType != 'radio'){
25433 Roo.bootstrap.CheckBox.superclass.disable.call(this);
25440 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25441 _this.getActionEl().addClass(this.disabledClass);
25442 e.dom.disabled = true;
25446 this.disabled = true;
25447 this.fireEvent("disable", this);
25451 enable : function()
25453 if(this.inputType != 'radio'){
25454 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25461 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25462 _this.getActionEl().removeClass(this.disabledClass);
25463 e.dom.disabled = false;
25467 this.disabled = false;
25468 this.fireEvent("enable", this);
25472 setBoxLabel : function(v)
25477 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25483 Roo.apply(Roo.bootstrap.CheckBox, {
25488 * register a CheckBox Group
25489 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25491 register : function(checkbox)
25493 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25494 this.groups[checkbox.groupId] = {};
25497 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25501 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25505 * fetch a CheckBox Group based on the group ID
25506 * @param {string} the group ID
25507 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25509 get: function(groupId) {
25510 if (typeof(this.groups[groupId]) == 'undefined') {
25514 return this.groups[groupId] ;
25527 * @class Roo.bootstrap.Radio
25528 * @extends Roo.bootstrap.Component
25529 * Bootstrap Radio class
25530 * @cfg {String} boxLabel - the label associated
25531 * @cfg {String} value - the value of radio
25534 * Create a new Radio
25535 * @param {Object} config The config object
25537 Roo.bootstrap.Radio = function(config){
25538 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25542 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25548 getAutoCreate : function()
25552 cls : 'form-group radio',
25557 html : this.boxLabel
25565 initEvents : function()
25567 this.parent().register(this);
25569 this.el.on('click', this.onClick, this);
25573 onClick : function(e)
25575 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25576 this.setChecked(true);
25580 setChecked : function(state, suppressEvent)
25582 this.parent().setValue(this.value, suppressEvent);
25586 setBoxLabel : function(v)
25591 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25606 * @class Roo.bootstrap.SecurePass
25607 * @extends Roo.bootstrap.Input
25608 * Bootstrap SecurePass class
25612 * Create a new SecurePass
25613 * @param {Object} config The config object
25616 Roo.bootstrap.SecurePass = function (config) {
25617 // these go here, so the translation tool can replace them..
25619 PwdEmpty: "Please type a password, and then retype it to confirm.",
25620 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25621 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25622 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25623 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25624 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25625 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25626 TooWeak: "Your password is Too Weak."
25628 this.meterLabel = "Password strength:";
25629 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25630 this.meterClass = [
25631 "roo-password-meter-tooweak",
25632 "roo-password-meter-weak",
25633 "roo-password-meter-medium",
25634 "roo-password-meter-strong",
25635 "roo-password-meter-grey"
25640 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25643 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25645 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25647 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25648 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25649 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25650 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25651 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25652 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25653 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25663 * @cfg {String/Object} Label for the strength meter (defaults to
25664 * 'Password strength:')
25669 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25670 * ['Weak', 'Medium', 'Strong'])
25673 pwdStrengths: false,
25686 initEvents: function ()
25688 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25690 if (this.el.is('input[type=password]') && Roo.isSafari) {
25691 this.el.on('keydown', this.SafariOnKeyDown, this);
25694 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25697 onRender: function (ct, position)
25699 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25700 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25701 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25703 this.trigger.createChild({
25708 cls: 'roo-password-meter-grey col-xs-12',
25711 //width: this.meterWidth + 'px'
25715 cls: 'roo-password-meter-text'
25721 if (this.hideTrigger) {
25722 this.trigger.setDisplayed(false);
25724 this.setSize(this.width || '', this.height || '');
25727 onDestroy: function ()
25729 if (this.trigger) {
25730 this.trigger.removeAllListeners();
25731 this.trigger.remove();
25734 this.wrap.remove();
25736 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25739 checkStrength: function ()
25741 var pwd = this.inputEl().getValue();
25742 if (pwd == this._lastPwd) {
25747 if (this.ClientSideStrongPassword(pwd)) {
25749 } else if (this.ClientSideMediumPassword(pwd)) {
25751 } else if (this.ClientSideWeakPassword(pwd)) {
25757 Roo.log('strength1: ' + strength);
25759 //var pm = this.trigger.child('div/div/div').dom;
25760 var pm = this.trigger.child('div/div');
25761 pm.removeClass(this.meterClass);
25762 pm.addClass(this.meterClass[strength]);
25765 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25767 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25769 this._lastPwd = pwd;
25773 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25775 this._lastPwd = '';
25777 var pm = this.trigger.child('div/div');
25778 pm.removeClass(this.meterClass);
25779 pm.addClass('roo-password-meter-grey');
25782 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25785 this.inputEl().dom.type='password';
25788 validateValue: function (value)
25790 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25793 if (value.length == 0) {
25794 if (this.allowBlank) {
25795 this.clearInvalid();
25799 this.markInvalid(this.errors.PwdEmpty);
25800 this.errorMsg = this.errors.PwdEmpty;
25808 if (!value.match(/[\x21-\x7e]+/)) {
25809 this.markInvalid(this.errors.PwdBadChar);
25810 this.errorMsg = this.errors.PwdBadChar;
25813 if (value.length < 6) {
25814 this.markInvalid(this.errors.PwdShort);
25815 this.errorMsg = this.errors.PwdShort;
25818 if (value.length > 16) {
25819 this.markInvalid(this.errors.PwdLong);
25820 this.errorMsg = this.errors.PwdLong;
25824 if (this.ClientSideStrongPassword(value)) {
25826 } else if (this.ClientSideMediumPassword(value)) {
25828 } else if (this.ClientSideWeakPassword(value)) {
25835 if (strength < 2) {
25836 //this.markInvalid(this.errors.TooWeak);
25837 this.errorMsg = this.errors.TooWeak;
25842 console.log('strength2: ' + strength);
25844 //var pm = this.trigger.child('div/div/div').dom;
25846 var pm = this.trigger.child('div/div');
25847 pm.removeClass(this.meterClass);
25848 pm.addClass(this.meterClass[strength]);
25850 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25852 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25854 this.errorMsg = '';
25858 CharacterSetChecks: function (type)
25861 this.fResult = false;
25864 isctype: function (character, type)
25867 case this.kCapitalLetter:
25868 if (character >= 'A' && character <= 'Z') {
25873 case this.kSmallLetter:
25874 if (character >= 'a' && character <= 'z') {
25880 if (character >= '0' && character <= '9') {
25885 case this.kPunctuation:
25886 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25897 IsLongEnough: function (pwd, size)
25899 return !(pwd == null || isNaN(size) || pwd.length < size);
25902 SpansEnoughCharacterSets: function (word, nb)
25904 if (!this.IsLongEnough(word, nb))
25909 var characterSetChecks = new Array(
25910 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25911 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25914 for (var index = 0; index < word.length; ++index) {
25915 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25916 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25917 characterSetChecks[nCharSet].fResult = true;
25924 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25925 if (characterSetChecks[nCharSet].fResult) {
25930 if (nCharSets < nb) {
25936 ClientSideStrongPassword: function (pwd)
25938 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25941 ClientSideMediumPassword: function (pwd)
25943 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25946 ClientSideWeakPassword: function (pwd)
25948 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25951 })//<script type="text/javascript">
25954 * Based Ext JS Library 1.1.1
25955 * Copyright(c) 2006-2007, Ext JS, LLC.
25961 * @class Roo.HtmlEditorCore
25962 * @extends Roo.Component
25963 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25965 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25968 Roo.HtmlEditorCore = function(config){
25971 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25976 * @event initialize
25977 * Fires when the editor is fully initialized (including the iframe)
25978 * @param {Roo.HtmlEditorCore} this
25983 * Fires when the editor is first receives the focus. Any insertion must wait
25984 * until after this event.
25985 * @param {Roo.HtmlEditorCore} this
25989 * @event beforesync
25990 * Fires before the textarea is updated with content from the editor iframe. Return false
25991 * to cancel the sync.
25992 * @param {Roo.HtmlEditorCore} this
25993 * @param {String} html
25997 * @event beforepush
25998 * Fires before the iframe editor is updated with content from the textarea. Return false
25999 * to cancel the push.
26000 * @param {Roo.HtmlEditorCore} this
26001 * @param {String} html
26006 * Fires when the textarea is updated with content from the editor iframe.
26007 * @param {Roo.HtmlEditorCore} this
26008 * @param {String} html
26013 * Fires when the iframe editor is updated with content from the textarea.
26014 * @param {Roo.HtmlEditorCore} this
26015 * @param {String} html
26020 * @event editorevent
26021 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26022 * @param {Roo.HtmlEditorCore} this
26028 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26030 // defaults : white / black...
26031 this.applyBlacklists();
26038 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
26042 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
26048 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26053 * @cfg {Number} height (in pixels)
26057 * @cfg {Number} width (in pixels)
26062 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26065 stylesheets: false,
26068 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26070 allowComments: false,
26074 // private properties
26075 validationEvent : false,
26077 initialized : false,
26079 sourceEditMode : false,
26080 onFocus : Roo.emptyFn,
26082 hideMode:'offsets',
26086 // blacklist + whitelisted elements..
26093 * Protected method that will not generally be called directly. It
26094 * is called when the editor initializes the iframe with HTML contents. Override this method if you
26095 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26097 getDocMarkup : function(){
26101 // inherit styels from page...??
26102 if (this.stylesheets === false) {
26104 Roo.get(document.head).select('style').each(function(node) {
26105 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26108 Roo.get(document.head).select('link').each(function(node) {
26109 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26112 } else if (!this.stylesheets.length) {
26114 st = '<style type="text/css">' +
26115 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26118 for (var i in this.stylesheets) {
26119 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26124 st += '<style type="text/css">' +
26125 'IMG { cursor: pointer } ' +
26128 var cls = 'roo-htmleditor-body';
26130 if(this.bodyCls.length){
26131 cls += ' ' + this.bodyCls;
26134 return '<html><head>' + st +
26135 //<style type="text/css">' +
26136 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26138 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
26142 onRender : function(ct, position)
26145 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26146 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26149 this.el.dom.style.border = '0 none';
26150 this.el.dom.setAttribute('tabIndex', -1);
26151 this.el.addClass('x-hidden hide');
26155 if(Roo.isIE){ // fix IE 1px bogus margin
26156 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26160 this.frameId = Roo.id();
26164 var iframe = this.owner.wrap.createChild({
26166 cls: 'form-control', // bootstrap..
26168 name: this.frameId,
26169 frameBorder : 'no',
26170 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
26175 this.iframe = iframe.dom;
26177 this.assignDocWin();
26179 this.doc.designMode = 'on';
26182 this.doc.write(this.getDocMarkup());
26186 var task = { // must defer to wait for browser to be ready
26188 //console.log("run task?" + this.doc.readyState);
26189 this.assignDocWin();
26190 if(this.doc.body || this.doc.readyState == 'complete'){
26192 this.doc.designMode="on";
26196 Roo.TaskMgr.stop(task);
26197 this.initEditor.defer(10, this);
26204 Roo.TaskMgr.start(task);
26209 onResize : function(w, h)
26211 Roo.log('resize: ' +w + ',' + h );
26212 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26216 if(typeof w == 'number'){
26218 this.iframe.style.width = w + 'px';
26220 if(typeof h == 'number'){
26222 this.iframe.style.height = h + 'px';
26224 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26231 * Toggles the editor between standard and source edit mode.
26232 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26234 toggleSourceEdit : function(sourceEditMode){
26236 this.sourceEditMode = sourceEditMode === true;
26238 if(this.sourceEditMode){
26240 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
26243 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26244 //this.iframe.className = '';
26247 //this.setSize(this.owner.wrap.getSize());
26248 //this.fireEvent('editmodechange', this, this.sourceEditMode);
26255 * Protected method that will not generally be called directly. If you need/want
26256 * custom HTML cleanup, this is the method you should override.
26257 * @param {String} html The HTML to be cleaned
26258 * return {String} The cleaned HTML
26260 cleanHtml : function(html){
26261 html = String(html);
26262 if(html.length > 5){
26263 if(Roo.isSafari){ // strip safari nonsense
26264 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26267 if(html == ' '){
26274 * HTML Editor -> Textarea
26275 * Protected method that will not generally be called directly. Syncs the contents
26276 * of the editor iframe with the textarea.
26278 syncValue : function(){
26279 if(this.initialized){
26280 var bd = (this.doc.body || this.doc.documentElement);
26281 //this.cleanUpPaste(); -- this is done else where and causes havoc..
26282 var html = bd.innerHTML;
26284 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26285 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26287 html = '<div style="'+m[0]+'">' + html + '</div>';
26290 html = this.cleanHtml(html);
26291 // fix up the special chars.. normaly like back quotes in word...
26292 // however we do not want to do this with chinese..
26293 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26295 var cc = match.charCodeAt();
26297 // Get the character value, handling surrogate pairs
26298 if (match.length == 2) {
26299 // It's a surrogate pair, calculate the Unicode code point
26300 var high = match.charCodeAt(0) - 0xD800;
26301 var low = match.charCodeAt(1) - 0xDC00;
26302 cc = (high * 0x400) + low + 0x10000;
26304 (cc >= 0x4E00 && cc < 0xA000 ) ||
26305 (cc >= 0x3400 && cc < 0x4E00 ) ||
26306 (cc >= 0xf900 && cc < 0xfb00 )
26311 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26312 return "&#" + cc + ";";
26319 if(this.owner.fireEvent('beforesync', this, html) !== false){
26320 this.el.dom.value = html;
26321 this.owner.fireEvent('sync', this, html);
26327 * Protected method that will not generally be called directly. Pushes the value of the textarea
26328 * into the iframe editor.
26330 pushValue : function(){
26331 if(this.initialized){
26332 var v = this.el.dom.value.trim();
26334 // if(v.length < 1){
26338 if(this.owner.fireEvent('beforepush', this, v) !== false){
26339 var d = (this.doc.body || this.doc.documentElement);
26341 this.cleanUpPaste();
26342 this.el.dom.value = d.innerHTML;
26343 this.owner.fireEvent('push', this, v);
26349 deferFocus : function(){
26350 this.focus.defer(10, this);
26354 focus : function(){
26355 if(this.win && !this.sourceEditMode){
26362 assignDocWin: function()
26364 var iframe = this.iframe;
26367 this.doc = iframe.contentWindow.document;
26368 this.win = iframe.contentWindow;
26370 // if (!Roo.get(this.frameId)) {
26373 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26374 // this.win = Roo.get(this.frameId).dom.contentWindow;
26376 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26380 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26381 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26386 initEditor : function(){
26387 //console.log("INIT EDITOR");
26388 this.assignDocWin();
26392 this.doc.designMode="on";
26394 this.doc.write(this.getDocMarkup());
26397 var dbody = (this.doc.body || this.doc.documentElement);
26398 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26399 // this copies styles from the containing element into thsi one..
26400 // not sure why we need all of this..
26401 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26403 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26404 //ss['background-attachment'] = 'fixed'; // w3c
26405 dbody.bgProperties = 'fixed'; // ie
26406 //Roo.DomHelper.applyStyles(dbody, ss);
26407 Roo.EventManager.on(this.doc, {
26408 //'mousedown': this.onEditorEvent,
26409 'mouseup': this.onEditorEvent,
26410 'dblclick': this.onEditorEvent,
26411 'click': this.onEditorEvent,
26412 'keyup': this.onEditorEvent,
26417 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26419 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26420 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26422 this.initialized = true;
26424 this.owner.fireEvent('initialize', this);
26429 onDestroy : function(){
26435 //for (var i =0; i < this.toolbars.length;i++) {
26436 // // fixme - ask toolbars for heights?
26437 // this.toolbars[i].onDestroy();
26440 //this.wrap.dom.innerHTML = '';
26441 //this.wrap.remove();
26446 onFirstFocus : function(){
26448 this.assignDocWin();
26451 this.activated = true;
26454 if(Roo.isGecko){ // prevent silly gecko errors
26456 var s = this.win.getSelection();
26457 if(!s.focusNode || s.focusNode.nodeType != 3){
26458 var r = s.getRangeAt(0);
26459 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26464 this.execCmd('useCSS', true);
26465 this.execCmd('styleWithCSS', false);
26468 this.owner.fireEvent('activate', this);
26472 adjustFont: function(btn){
26473 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26474 //if(Roo.isSafari){ // safari
26477 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26478 if(Roo.isSafari){ // safari
26479 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26480 v = (v < 10) ? 10 : v;
26481 v = (v > 48) ? 48 : v;
26482 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26487 v = Math.max(1, v+adjust);
26489 this.execCmd('FontSize', v );
26492 onEditorEvent : function(e)
26494 this.owner.fireEvent('editorevent', this, e);
26495 // this.updateToolbar();
26496 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26499 insertTag : function(tg)
26501 // could be a bit smarter... -> wrap the current selected tRoo..
26502 if (tg.toLowerCase() == 'span' ||
26503 tg.toLowerCase() == 'code' ||
26504 tg.toLowerCase() == 'sup' ||
26505 tg.toLowerCase() == 'sub'
26508 range = this.createRange(this.getSelection());
26509 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26510 wrappingNode.appendChild(range.extractContents());
26511 range.insertNode(wrappingNode);
26518 this.execCmd("formatblock", tg);
26522 insertText : function(txt)
26526 var range = this.createRange();
26527 range.deleteContents();
26528 //alert(Sender.getAttribute('label'));
26530 range.insertNode(this.doc.createTextNode(txt));
26536 * Executes a Midas editor command on the editor document and performs necessary focus and
26537 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26538 * @param {String} cmd The Midas command
26539 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26541 relayCmd : function(cmd, value){
26543 this.execCmd(cmd, value);
26544 this.owner.fireEvent('editorevent', this);
26545 //this.updateToolbar();
26546 this.owner.deferFocus();
26550 * Executes a Midas editor command directly on the editor document.
26551 * For visual commands, you should use {@link #relayCmd} instead.
26552 * <b>This should only be called after the editor is initialized.</b>
26553 * @param {String} cmd The Midas command
26554 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26556 execCmd : function(cmd, value){
26557 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26564 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26566 * @param {String} text | dom node..
26568 insertAtCursor : function(text)
26571 if(!this.activated){
26577 var r = this.doc.selection.createRange();
26588 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26592 // from jquery ui (MIT licenced)
26594 var win = this.win;
26596 if (win.getSelection && win.getSelection().getRangeAt) {
26597 range = win.getSelection().getRangeAt(0);
26598 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26599 range.insertNode(node);
26600 } else if (win.document.selection && win.document.selection.createRange) {
26601 // no firefox support
26602 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26603 win.document.selection.createRange().pasteHTML(txt);
26605 // no firefox support
26606 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26607 this.execCmd('InsertHTML', txt);
26616 mozKeyPress : function(e){
26618 var c = e.getCharCode(), cmd;
26621 c = String.fromCharCode(c).toLowerCase();
26635 this.cleanUpPaste.defer(100, this);
26643 e.preventDefault();
26651 fixKeys : function(){ // load time branching for fastest keydown performance
26653 return function(e){
26654 var k = e.getKey(), r;
26657 r = this.doc.selection.createRange();
26660 r.pasteHTML('    ');
26667 r = this.doc.selection.createRange();
26669 var target = r.parentElement();
26670 if(!target || target.tagName.toLowerCase() != 'li'){
26672 r.pasteHTML('<br />');
26678 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26679 this.cleanUpPaste.defer(100, this);
26685 }else if(Roo.isOpera){
26686 return function(e){
26687 var k = e.getKey();
26691 this.execCmd('InsertHTML','    ');
26694 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26695 this.cleanUpPaste.defer(100, this);
26700 }else if(Roo.isSafari){
26701 return function(e){
26702 var k = e.getKey();
26706 this.execCmd('InsertText','\t');
26710 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26711 this.cleanUpPaste.defer(100, this);
26719 getAllAncestors: function()
26721 var p = this.getSelectedNode();
26724 a.push(p); // push blank onto stack..
26725 p = this.getParentElement();
26729 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26733 a.push(this.doc.body);
26737 lastSelNode : false,
26740 getSelection : function()
26742 this.assignDocWin();
26743 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26746 getSelectedNode: function()
26748 // this may only work on Gecko!!!
26750 // should we cache this!!!!
26755 var range = this.createRange(this.getSelection()).cloneRange();
26758 var parent = range.parentElement();
26760 var testRange = range.duplicate();
26761 testRange.moveToElementText(parent);
26762 if (testRange.inRange(range)) {
26765 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26768 parent = parent.parentElement;
26773 // is ancestor a text element.
26774 var ac = range.commonAncestorContainer;
26775 if (ac.nodeType == 3) {
26776 ac = ac.parentNode;
26779 var ar = ac.childNodes;
26782 var other_nodes = [];
26783 var has_other_nodes = false;
26784 for (var i=0;i<ar.length;i++) {
26785 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26788 // fullly contained node.
26790 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26795 // probably selected..
26796 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26797 other_nodes.push(ar[i]);
26801 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26806 has_other_nodes = true;
26808 if (!nodes.length && other_nodes.length) {
26809 nodes= other_nodes;
26811 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26817 createRange: function(sel)
26819 // this has strange effects when using with
26820 // top toolbar - not sure if it's a great idea.
26821 //this.editor.contentWindow.focus();
26822 if (typeof sel != "undefined") {
26824 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26826 return this.doc.createRange();
26829 return this.doc.createRange();
26832 getParentElement: function()
26835 this.assignDocWin();
26836 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26838 var range = this.createRange(sel);
26841 var p = range.commonAncestorContainer;
26842 while (p.nodeType == 3) { // text node
26853 * Range intersection.. the hard stuff...
26857 * [ -- selected range --- ]
26861 * if end is before start or hits it. fail.
26862 * if start is after end or hits it fail.
26864 * if either hits (but other is outside. - then it's not
26870 // @see http://www.thismuchiknow.co.uk/?p=64.
26871 rangeIntersectsNode : function(range, node)
26873 var nodeRange = node.ownerDocument.createRange();
26875 nodeRange.selectNode(node);
26877 nodeRange.selectNodeContents(node);
26880 var rangeStartRange = range.cloneRange();
26881 rangeStartRange.collapse(true);
26883 var rangeEndRange = range.cloneRange();
26884 rangeEndRange.collapse(false);
26886 var nodeStartRange = nodeRange.cloneRange();
26887 nodeStartRange.collapse(true);
26889 var nodeEndRange = nodeRange.cloneRange();
26890 nodeEndRange.collapse(false);
26892 return rangeStartRange.compareBoundaryPoints(
26893 Range.START_TO_START, nodeEndRange) == -1 &&
26894 rangeEndRange.compareBoundaryPoints(
26895 Range.START_TO_START, nodeStartRange) == 1;
26899 rangeCompareNode : function(range, node)
26901 var nodeRange = node.ownerDocument.createRange();
26903 nodeRange.selectNode(node);
26905 nodeRange.selectNodeContents(node);
26909 range.collapse(true);
26911 nodeRange.collapse(true);
26913 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26914 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26916 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26918 var nodeIsBefore = ss == 1;
26919 var nodeIsAfter = ee == -1;
26921 if (nodeIsBefore && nodeIsAfter) {
26924 if (!nodeIsBefore && nodeIsAfter) {
26925 return 1; //right trailed.
26928 if (nodeIsBefore && !nodeIsAfter) {
26929 return 2; // left trailed.
26935 // private? - in a new class?
26936 cleanUpPaste : function()
26938 // cleans up the whole document..
26939 Roo.log('cleanuppaste');
26941 this.cleanUpChildren(this.doc.body);
26942 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26943 if (clean != this.doc.body.innerHTML) {
26944 this.doc.body.innerHTML = clean;
26949 cleanWordChars : function(input) {// change the chars to hex code
26950 var he = Roo.HtmlEditorCore;
26952 var output = input;
26953 Roo.each(he.swapCodes, function(sw) {
26954 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26956 output = output.replace(swapper, sw[1]);
26963 cleanUpChildren : function (n)
26965 if (!n.childNodes.length) {
26968 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26969 this.cleanUpChild(n.childNodes[i]);
26976 cleanUpChild : function (node)
26979 //console.log(node);
26980 if (node.nodeName == "#text") {
26981 // clean up silly Windows -- stuff?
26984 if (node.nodeName == "#comment") {
26985 if (!this.allowComments) {
26986 node.parentNode.removeChild(node);
26988 // clean up silly Windows -- stuff?
26991 var lcname = node.tagName.toLowerCase();
26992 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26993 // whitelist of tags..
26995 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26997 node.parentNode.removeChild(node);
27002 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27004 // spans with no attributes - just remove them..
27005 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
27006 remove_keep_children = true;
27009 // remove <a name=....> as rendering on yahoo mailer is borked with this.
27010 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27012 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27013 // remove_keep_children = true;
27016 if (remove_keep_children) {
27017 this.cleanUpChildren(node);
27018 // inserts everything just before this node...
27019 while (node.childNodes.length) {
27020 var cn = node.childNodes[0];
27021 node.removeChild(cn);
27022 node.parentNode.insertBefore(cn, node);
27024 node.parentNode.removeChild(node);
27028 if (!node.attributes || !node.attributes.length) {
27033 this.cleanUpChildren(node);
27037 function cleanAttr(n,v)
27040 if (v.match(/^\./) || v.match(/^\//)) {
27043 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27046 if (v.match(/^#/)) {
27049 if (v.match(/^\{/)) { // allow template editing.
27052 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27053 node.removeAttribute(n);
27057 var cwhite = this.cwhite;
27058 var cblack = this.cblack;
27060 function cleanStyle(n,v)
27062 if (v.match(/expression/)) { //XSS?? should we even bother..
27063 node.removeAttribute(n);
27067 var parts = v.split(/;/);
27070 Roo.each(parts, function(p) {
27071 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27075 var l = p.split(':').shift().replace(/\s+/g,'');
27076 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27078 if ( cwhite.length && cblack.indexOf(l) > -1) {
27079 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27080 //node.removeAttribute(n);
27084 // only allow 'c whitelisted system attributes'
27085 if ( cwhite.length && cwhite.indexOf(l) < 0) {
27086 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27087 //node.removeAttribute(n);
27097 if (clean.length) {
27098 node.setAttribute(n, clean.join(';'));
27100 node.removeAttribute(n);
27106 for (var i = node.attributes.length-1; i > -1 ; i--) {
27107 var a = node.attributes[i];
27110 if (a.name.toLowerCase().substr(0,2)=='on') {
27111 node.removeAttribute(a.name);
27114 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27115 node.removeAttribute(a.name);
27118 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27119 cleanAttr(a.name,a.value); // fixme..
27122 if (a.name == 'style') {
27123 cleanStyle(a.name,a.value);
27126 /// clean up MS crap..
27127 // tecnically this should be a list of valid class'es..
27130 if (a.name == 'class') {
27131 if (a.value.match(/^Mso/)) {
27132 node.removeAttribute('class');
27135 if (a.value.match(/^body$/)) {
27136 node.removeAttribute('class');
27147 this.cleanUpChildren(node);
27153 * Clean up MS wordisms...
27155 cleanWord : function(node)
27158 this.cleanWord(this.doc.body);
27163 node.nodeName == 'SPAN' &&
27164 !node.hasAttributes() &&
27165 node.childNodes.length == 1 &&
27166 node.firstChild.nodeName == "#text"
27168 var textNode = node.firstChild;
27169 node.removeChild(textNode);
27170 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27171 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27173 node.parentNode.insertBefore(textNode, node);
27174 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27175 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27177 node.parentNode.removeChild(node);
27180 if (node.nodeName == "#text") {
27181 // clean up silly Windows -- stuff?
27184 if (node.nodeName == "#comment") {
27185 node.parentNode.removeChild(node);
27186 // clean up silly Windows -- stuff?
27190 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27191 node.parentNode.removeChild(node);
27194 //Roo.log(node.tagName);
27195 // remove - but keep children..
27196 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27197 //Roo.log('-- removed');
27198 while (node.childNodes.length) {
27199 var cn = node.childNodes[0];
27200 node.removeChild(cn);
27201 node.parentNode.insertBefore(cn, node);
27202 // move node to parent - and clean it..
27203 this.cleanWord(cn);
27205 node.parentNode.removeChild(node);
27206 /// no need to iterate chidlren = it's got none..
27207 //this.iterateChildren(node, this.cleanWord);
27211 if (node.className.length) {
27213 var cn = node.className.split(/\W+/);
27215 Roo.each(cn, function(cls) {
27216 if (cls.match(/Mso[a-zA-Z]+/)) {
27221 node.className = cna.length ? cna.join(' ') : '';
27223 node.removeAttribute("class");
27227 if (node.hasAttribute("lang")) {
27228 node.removeAttribute("lang");
27231 if (node.hasAttribute("style")) {
27233 var styles = node.getAttribute("style").split(";");
27235 Roo.each(styles, function(s) {
27236 if (!s.match(/:/)) {
27239 var kv = s.split(":");
27240 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27243 // what ever is left... we allow.
27246 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27247 if (!nstyle.length) {
27248 node.removeAttribute('style');
27251 this.iterateChildren(node, this.cleanWord);
27257 * iterateChildren of a Node, calling fn each time, using this as the scole..
27258 * @param {DomNode} node node to iterate children of.
27259 * @param {Function} fn method of this class to call on each item.
27261 iterateChildren : function(node, fn)
27263 if (!node.childNodes.length) {
27266 for (var i = node.childNodes.length-1; i > -1 ; i--) {
27267 fn.call(this, node.childNodes[i])
27273 * cleanTableWidths.
27275 * Quite often pasting from word etc.. results in tables with column and widths.
27276 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27279 cleanTableWidths : function(node)
27284 this.cleanTableWidths(this.doc.body);
27289 if (node.nodeName == "#text" || node.nodeName == "#comment") {
27292 Roo.log(node.tagName);
27293 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27294 this.iterateChildren(node, this.cleanTableWidths);
27297 if (node.hasAttribute('width')) {
27298 node.removeAttribute('width');
27302 if (node.hasAttribute("style")) {
27305 var styles = node.getAttribute("style").split(";");
27307 Roo.each(styles, function(s) {
27308 if (!s.match(/:/)) {
27311 var kv = s.split(":");
27312 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27315 // what ever is left... we allow.
27318 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27319 if (!nstyle.length) {
27320 node.removeAttribute('style');
27324 this.iterateChildren(node, this.cleanTableWidths);
27332 domToHTML : function(currentElement, depth, nopadtext) {
27334 depth = depth || 0;
27335 nopadtext = nopadtext || false;
27337 if (!currentElement) {
27338 return this.domToHTML(this.doc.body);
27341 //Roo.log(currentElement);
27343 var allText = false;
27344 var nodeName = currentElement.nodeName;
27345 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27347 if (nodeName == '#text') {
27349 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27354 if (nodeName != 'BODY') {
27357 // Prints the node tagName, such as <A>, <IMG>, etc
27360 for(i = 0; i < currentElement.attributes.length;i++) {
27362 var aname = currentElement.attributes.item(i).name;
27363 if (!currentElement.attributes.item(i).value.length) {
27366 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27369 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27378 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27381 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27386 // Traverse the tree
27388 var currentElementChild = currentElement.childNodes.item(i);
27389 var allText = true;
27390 var innerHTML = '';
27392 while (currentElementChild) {
27393 // Formatting code (indent the tree so it looks nice on the screen)
27394 var nopad = nopadtext;
27395 if (lastnode == 'SPAN') {
27399 if (currentElementChild.nodeName == '#text') {
27400 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27401 toadd = nopadtext ? toadd : toadd.trim();
27402 if (!nopad && toadd.length > 80) {
27403 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27405 innerHTML += toadd;
27408 currentElementChild = currentElement.childNodes.item(i);
27414 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27416 // Recursively traverse the tree structure of the child node
27417 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27418 lastnode = currentElementChild.nodeName;
27420 currentElementChild=currentElement.childNodes.item(i);
27426 // The remaining code is mostly for formatting the tree
27427 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27432 ret+= "</"+tagName+">";
27438 applyBlacklists : function()
27440 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27441 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27445 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27446 if (b.indexOf(tag) > -1) {
27449 this.white.push(tag);
27453 Roo.each(w, function(tag) {
27454 if (b.indexOf(tag) > -1) {
27457 if (this.white.indexOf(tag) > -1) {
27460 this.white.push(tag);
27465 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27466 if (w.indexOf(tag) > -1) {
27469 this.black.push(tag);
27473 Roo.each(b, function(tag) {
27474 if (w.indexOf(tag) > -1) {
27477 if (this.black.indexOf(tag) > -1) {
27480 this.black.push(tag);
27485 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27486 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27490 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27491 if (b.indexOf(tag) > -1) {
27494 this.cwhite.push(tag);
27498 Roo.each(w, function(tag) {
27499 if (b.indexOf(tag) > -1) {
27502 if (this.cwhite.indexOf(tag) > -1) {
27505 this.cwhite.push(tag);
27510 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27511 if (w.indexOf(tag) > -1) {
27514 this.cblack.push(tag);
27518 Roo.each(b, function(tag) {
27519 if (w.indexOf(tag) > -1) {
27522 if (this.cblack.indexOf(tag) > -1) {
27525 this.cblack.push(tag);
27530 setStylesheets : function(stylesheets)
27532 if(typeof(stylesheets) == 'string'){
27533 Roo.get(this.iframe.contentDocument.head).createChild({
27535 rel : 'stylesheet',
27544 Roo.each(stylesheets, function(s) {
27549 Roo.get(_this.iframe.contentDocument.head).createChild({
27551 rel : 'stylesheet',
27560 removeStylesheets : function()
27564 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27569 setStyle : function(style)
27571 Roo.get(this.iframe.contentDocument.head).createChild({
27580 // hide stuff that is not compatible
27594 * @event specialkey
27598 * @cfg {String} fieldClass @hide
27601 * @cfg {String} focusClass @hide
27604 * @cfg {String} autoCreate @hide
27607 * @cfg {String} inputType @hide
27610 * @cfg {String} invalidClass @hide
27613 * @cfg {String} invalidText @hide
27616 * @cfg {String} msgFx @hide
27619 * @cfg {String} validateOnBlur @hide
27623 Roo.HtmlEditorCore.white = [
27624 'area', 'br', 'img', 'input', 'hr', 'wbr',
27626 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27627 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27628 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27629 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27630 'table', 'ul', 'xmp',
27632 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27635 'dir', 'menu', 'ol', 'ul', 'dl',
27641 Roo.HtmlEditorCore.black = [
27642 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27644 'base', 'basefont', 'bgsound', 'blink', 'body',
27645 'frame', 'frameset', 'head', 'html', 'ilayer',
27646 'iframe', 'layer', 'link', 'meta', 'object',
27647 'script', 'style' ,'title', 'xml' // clean later..
27649 Roo.HtmlEditorCore.clean = [
27650 'script', 'style', 'title', 'xml'
27652 Roo.HtmlEditorCore.remove = [
27657 Roo.HtmlEditorCore.ablack = [
27661 Roo.HtmlEditorCore.aclean = [
27662 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27666 Roo.HtmlEditorCore.pwhite= [
27667 'http', 'https', 'mailto'
27670 // white listed style attributes.
27671 Roo.HtmlEditorCore.cwhite= [
27672 // 'text-align', /// default is to allow most things..
27678 // black listed style attributes.
27679 Roo.HtmlEditorCore.cblack= [
27680 // 'font-size' -- this can be set by the project
27684 Roo.HtmlEditorCore.swapCodes =[
27685 [ 8211, "–" ],
27686 [ 8212, "—" ],
27703 * @class Roo.bootstrap.HtmlEditor
27704 * @extends Roo.bootstrap.TextArea
27705 * Bootstrap HtmlEditor class
27708 * Create a new HtmlEditor
27709 * @param {Object} config The config object
27712 Roo.bootstrap.HtmlEditor = function(config){
27713 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27714 if (!this.toolbars) {
27715 this.toolbars = [];
27718 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27721 * @event initialize
27722 * Fires when the editor is fully initialized (including the iframe)
27723 * @param {HtmlEditor} this
27728 * Fires when the editor is first receives the focus. Any insertion must wait
27729 * until after this event.
27730 * @param {HtmlEditor} this
27734 * @event beforesync
27735 * Fires before the textarea is updated with content from the editor iframe. Return false
27736 * to cancel the sync.
27737 * @param {HtmlEditor} this
27738 * @param {String} html
27742 * @event beforepush
27743 * Fires before the iframe editor is updated with content from the textarea. Return false
27744 * to cancel the push.
27745 * @param {HtmlEditor} this
27746 * @param {String} html
27751 * Fires when the textarea is updated with content from the editor iframe.
27752 * @param {HtmlEditor} this
27753 * @param {String} html
27758 * Fires when the iframe editor is updated with content from the textarea.
27759 * @param {HtmlEditor} this
27760 * @param {String} html
27764 * @event editmodechange
27765 * Fires when the editor switches edit modes
27766 * @param {HtmlEditor} this
27767 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27769 editmodechange: true,
27771 * @event editorevent
27772 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27773 * @param {HtmlEditor} this
27777 * @event firstfocus
27778 * Fires when on first focus - needed by toolbars..
27779 * @param {HtmlEditor} this
27784 * Auto save the htmlEditor value as a file into Events
27785 * @param {HtmlEditor} this
27789 * @event savedpreview
27790 * preview the saved version of htmlEditor
27791 * @param {HtmlEditor} this
27798 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27802 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27807 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27812 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27817 * @cfg {Number} height (in pixels)
27821 * @cfg {Number} width (in pixels)
27826 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27829 stylesheets: false,
27834 // private properties
27835 validationEvent : false,
27837 initialized : false,
27840 onFocus : Roo.emptyFn,
27842 hideMode:'offsets',
27844 tbContainer : false,
27848 toolbarContainer :function() {
27849 return this.wrap.select('.x-html-editor-tb',true).first();
27853 * Protected method that will not generally be called directly. It
27854 * is called when the editor creates its toolbar. Override this method if you need to
27855 * add custom toolbar buttons.
27856 * @param {HtmlEditor} editor
27858 createToolbar : function(){
27859 Roo.log('renewing');
27860 Roo.log("create toolbars");
27862 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27863 this.toolbars[0].render(this.toolbarContainer());
27867 // if (!editor.toolbars || !editor.toolbars.length) {
27868 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27871 // for (var i =0 ; i < editor.toolbars.length;i++) {
27872 // editor.toolbars[i] = Roo.factory(
27873 // typeof(editor.toolbars[i]) == 'string' ?
27874 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27875 // Roo.bootstrap.HtmlEditor);
27876 // editor.toolbars[i].init(editor);
27882 onRender : function(ct, position)
27884 // Roo.log("Call onRender: " + this.xtype);
27886 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27888 this.wrap = this.inputEl().wrap({
27889 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27892 this.editorcore.onRender(ct, position);
27894 if (this.resizable) {
27895 this.resizeEl = new Roo.Resizable(this.wrap, {
27899 minHeight : this.height,
27900 height: this.height,
27901 handles : this.resizable,
27904 resize : function(r, w, h) {
27905 _t.onResize(w,h); // -something
27911 this.createToolbar(this);
27914 if(!this.width && this.resizable){
27915 this.setSize(this.wrap.getSize());
27917 if (this.resizeEl) {
27918 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27919 // should trigger onReize..
27925 onResize : function(w, h)
27927 Roo.log('resize: ' +w + ',' + h );
27928 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27932 if(this.inputEl() ){
27933 if(typeof w == 'number'){
27934 var aw = w - this.wrap.getFrameWidth('lr');
27935 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27938 if(typeof h == 'number'){
27939 var tbh = -11; // fixme it needs to tool bar size!
27940 for (var i =0; i < this.toolbars.length;i++) {
27941 // fixme - ask toolbars for heights?
27942 tbh += this.toolbars[i].el.getHeight();
27943 //if (this.toolbars[i].footer) {
27944 // tbh += this.toolbars[i].footer.el.getHeight();
27952 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27953 ah -= 5; // knock a few pixes off for look..
27954 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27958 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27959 this.editorcore.onResize(ew,eh);
27964 * Toggles the editor between standard and source edit mode.
27965 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27967 toggleSourceEdit : function(sourceEditMode)
27969 this.editorcore.toggleSourceEdit(sourceEditMode);
27971 if(this.editorcore.sourceEditMode){
27972 Roo.log('editor - showing textarea');
27975 // Roo.log(this.syncValue());
27977 this.inputEl().removeClass(['hide', 'x-hidden']);
27978 this.inputEl().dom.removeAttribute('tabIndex');
27979 this.inputEl().focus();
27981 Roo.log('editor - hiding textarea');
27983 // Roo.log(this.pushValue());
27986 this.inputEl().addClass(['hide', 'x-hidden']);
27987 this.inputEl().dom.setAttribute('tabIndex', -1);
27988 //this.deferFocus();
27991 if(this.resizable){
27992 this.setSize(this.wrap.getSize());
27995 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27998 // private (for BoxComponent)
27999 adjustSize : Roo.BoxComponent.prototype.adjustSize,
28001 // private (for BoxComponent)
28002 getResizeEl : function(){
28006 // private (for BoxComponent)
28007 getPositionEl : function(){
28012 initEvents : function(){
28013 this.originalValue = this.getValue();
28017 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28020 // markInvalid : Roo.emptyFn,
28022 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28025 // clearInvalid : Roo.emptyFn,
28027 setValue : function(v){
28028 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
28029 this.editorcore.pushValue();
28034 deferFocus : function(){
28035 this.focus.defer(10, this);
28039 focus : function(){
28040 this.editorcore.focus();
28046 onDestroy : function(){
28052 for (var i =0; i < this.toolbars.length;i++) {
28053 // fixme - ask toolbars for heights?
28054 this.toolbars[i].onDestroy();
28057 this.wrap.dom.innerHTML = '';
28058 this.wrap.remove();
28063 onFirstFocus : function(){
28064 //Roo.log("onFirstFocus");
28065 this.editorcore.onFirstFocus();
28066 for (var i =0; i < this.toolbars.length;i++) {
28067 this.toolbars[i].onFirstFocus();
28073 syncValue : function()
28075 this.editorcore.syncValue();
28078 pushValue : function()
28080 this.editorcore.pushValue();
28084 // hide stuff that is not compatible
28098 * @event specialkey
28102 * @cfg {String} fieldClass @hide
28105 * @cfg {String} focusClass @hide
28108 * @cfg {String} autoCreate @hide
28111 * @cfg {String} inputType @hide
28115 * @cfg {String} invalidText @hide
28118 * @cfg {String} msgFx @hide
28121 * @cfg {String} validateOnBlur @hide
28130 Roo.namespace('Roo.bootstrap.htmleditor');
28132 * @class Roo.bootstrap.HtmlEditorToolbar1
28133 * @extends Roo.bootstrap.nav.Simplebar
28139 new Roo.bootstrap.HtmlEditor({
28142 new Roo.bootstrap.HtmlEditorToolbar1({
28143 disable : { fonts: 1 , format: 1, ..., ... , ...],
28149 * @cfg {Object} disable List of elements to disable..
28150 * @cfg {Array} btns List of additional buttons.
28154 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28157 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
28160 Roo.apply(this, config);
28162 // default disabled, based on 'good practice'..
28163 this.disable = this.disable || {};
28164 Roo.applyIf(this.disable, {
28167 specialElements : true
28169 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
28171 this.editor = config.editor;
28172 this.editorcore = config.editor.editorcore;
28174 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28176 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28177 // dont call parent... till later.
28179 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.nav.Simplebar, {
28184 editorcore : false,
28189 "h1","h2","h3","h4","h5","h6",
28191 "abbr", "acronym", "address", "cite", "samp", "var",
28195 onRender : function(ct, position)
28197 // Roo.log("Call onRender: " + this.xtype);
28199 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
28201 this.el.dom.style.marginBottom = '0';
28203 var editorcore = this.editorcore;
28204 var editor= this.editor;
28207 var btn = function(id,cmd , toggle, handler, html){
28209 var event = toggle ? 'toggle' : 'click';
28214 xns: Roo.bootstrap,
28218 enableToggle:toggle !== false,
28220 pressed : toggle ? false : null,
28223 a.listeners[toggle ? 'toggle' : 'click'] = function() {
28224 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
28230 // var cb_box = function...
28235 xns: Roo.bootstrap,
28240 xns: Roo.bootstrap,
28244 Roo.each(this.formats, function(f) {
28245 style.menu.items.push({
28247 xns: Roo.bootstrap,
28248 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28253 editorcore.insertTag(this.tagname);
28260 children.push(style);
28262 btn('bold',false,true);
28263 btn('italic',false,true);
28264 btn('align-left', 'justifyleft',true);
28265 btn('align-center', 'justifycenter',true);
28266 btn('align-right' , 'justifyright',true);
28267 btn('link', false, false, function(btn) {
28268 //Roo.log("create link?");
28269 var url = prompt(this.createLinkText, this.defaultLinkValue);
28270 if(url && url != 'http:/'+'/'){
28271 this.editorcore.relayCmd('createlink', url);
28274 btn('list','insertunorderedlist',true);
28275 btn('pencil', false,true, function(btn){
28277 this.toggleSourceEdit(btn.pressed);
28280 if (this.editor.btns.length > 0) {
28281 for (var i = 0; i<this.editor.btns.length; i++) {
28282 children.push(this.editor.btns[i]);
28290 xns: Roo.bootstrap,
28295 xns: Roo.bootstrap,
28300 cog.menu.items.push({
28302 xns: Roo.bootstrap,
28303 html : Clean styles,
28308 editorcore.insertTag(this.tagname);
28317 this.xtype = 'NavSimplebar';
28319 for(var i=0;i< children.length;i++) {
28321 this.buttons.add(this.addxtypeChild(children[i]));
28325 editor.on('editorevent', this.updateToolbar, this);
28327 onBtnClick : function(id)
28329 this.editorcore.relayCmd(id);
28330 this.editorcore.focus();
28334 * Protected method that will not generally be called directly. It triggers
28335 * a toolbar update by reading the markup state of the current selection in the editor.
28337 updateToolbar: function(){
28339 if(!this.editorcore.activated){
28340 this.editor.onFirstFocus(); // is this neeed?
28344 var btns = this.buttons;
28345 var doc = this.editorcore.doc;
28346 btns.get('bold').setActive(doc.queryCommandState('bold'));
28347 btns.get('italic').setActive(doc.queryCommandState('italic'));
28348 //btns.get('underline').setActive(doc.queryCommandState('underline'));
28350 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28351 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28352 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28354 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28355 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28358 var ans = this.editorcore.getAllAncestors();
28359 if (this.formatCombo) {
28362 var store = this.formatCombo.store;
28363 this.formatCombo.setValue("");
28364 for (var i =0; i < ans.length;i++) {
28365 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28367 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28375 // hides menus... - so this cant be on a menu...
28376 Roo.bootstrap.MenuMgr.hideAll();
28378 Roo.bootstrap.menu.Manager.hideAll();
28379 //this.editorsyncValue();
28381 onFirstFocus: function() {
28382 this.buttons.each(function(item){
28386 toggleSourceEdit : function(sourceEditMode){
28389 if(sourceEditMode){
28390 Roo.log("disabling buttons");
28391 this.buttons.each( function(item){
28392 if(item.cmd != 'pencil'){
28398 Roo.log("enabling buttons");
28399 if(this.editorcore.initialized){
28400 this.buttons.each( function(item){
28406 Roo.log("calling toggole on editor");
28407 // tell the editor that it's been pressed..
28408 this.editor.toggleSourceEdit(sourceEditMode);
28422 * @class Roo.bootstrap.Markdown
28423 * @extends Roo.bootstrap.TextArea
28424 * Bootstrap Showdown editable area
28425 * @cfg {string} content
28428 * Create a new Showdown
28431 Roo.bootstrap.Markdown = function(config){
28432 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28436 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
28440 initEvents : function()
28443 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28444 this.markdownEl = this.el.createChild({
28445 cls : 'roo-markdown-area'
28447 this.inputEl().addClass('d-none');
28448 if (this.getValue() == '') {
28449 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28452 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28454 this.markdownEl.on('click', this.toggleTextEdit, this);
28455 this.on('blur', this.toggleTextEdit, this);
28456 this.on('specialkey', this.resizeTextArea, this);
28459 toggleTextEdit : function()
28461 var sh = this.markdownEl.getHeight();
28462 this.inputEl().addClass('d-none');
28463 this.markdownEl.addClass('d-none');
28464 if (!this.editing) {
28466 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28467 this.inputEl().removeClass('d-none');
28468 this.inputEl().focus();
28469 this.editing = true;
28472 // show showdown...
28473 this.updateMarkdown();
28474 this.markdownEl.removeClass('d-none');
28475 this.editing = false;
28478 updateMarkdown : function()
28480 if (this.getValue() == '') {
28481 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28485 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28488 resizeTextArea: function () {
28491 Roo.log([sh, this.getValue().split("\n").length * 30]);
28492 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28494 setValue : function(val)
28496 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28497 if (!this.editing) {
28498 this.updateMarkdown();
28504 if (!this.editing) {
28505 this.toggleTextEdit();
28513 * Ext JS Library 1.1.1
28514 * Copyright(c) 2006-2007, Ext JS, LLC.
28516 * Originally Released Under LGPL - original licence link has changed is not relivant.
28519 * <script type="text/javascript">
28523 * @class Roo.bootstrap.PagingToolbar
28524 * @extends Roo.bootstrap.nav.Simplebar
28525 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28527 * Create a new PagingToolbar
28528 * @param {Object} config The config object
28529 * @param {Roo.data.Store} store
28531 Roo.bootstrap.PagingToolbar = function(config)
28533 // old args format still supported... - xtype is prefered..
28534 // created from xtype...
28536 this.ds = config.dataSource;
28538 if (config.store && !this.ds) {
28539 this.store= Roo.factory(config.store, Roo.data);
28540 this.ds = this.store;
28541 this.ds.xmodule = this.xmodule || false;
28544 this.toolbarItems = [];
28545 if (config.items) {
28546 this.toolbarItems = config.items;
28549 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28554 this.bind(this.ds);
28557 if (Roo.bootstrap.version == 4) {
28558 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28560 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28565 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28567 * @cfg {Roo.bootstrap.Button} buttons[]
28568 * Buttons for the toolbar
28571 * @cfg {Roo.data.Store} store
28572 * The underlying data store providing the paged data
28575 * @cfg {String/HTMLElement/Element} container
28576 * container The id or element that will contain the toolbar
28579 * @cfg {Boolean} displayInfo
28580 * True to display the displayMsg (defaults to false)
28583 * @cfg {Number} pageSize
28584 * The number of records to display per page (defaults to 20)
28588 * @cfg {String} displayMsg
28589 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28591 displayMsg : 'Displaying {0} - {1} of {2}',
28593 * @cfg {String} emptyMsg
28594 * The message to display when no records are found (defaults to "No data to display")
28596 emptyMsg : 'No data to display',
28598 * Customizable piece of the default paging text (defaults to "Page")
28601 beforePageText : "Page",
28603 * Customizable piece of the default paging text (defaults to "of %0")
28606 afterPageText : "of {0}",
28608 * Customizable piece of the default paging text (defaults to "First Page")
28611 firstText : "First Page",
28613 * Customizable piece of the default paging text (defaults to "Previous Page")
28616 prevText : "Previous Page",
28618 * Customizable piece of the default paging text (defaults to "Next Page")
28621 nextText : "Next Page",
28623 * Customizable piece of the default paging text (defaults to "Last Page")
28626 lastText : "Last Page",
28628 * Customizable piece of the default paging text (defaults to "Refresh")
28631 refreshText : "Refresh",
28635 onRender : function(ct, position)
28637 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28638 this.navgroup.parentId = this.id;
28639 this.navgroup.onRender(this.el, null);
28640 // add the buttons to the navgroup
28642 if(this.displayInfo){
28643 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28644 this.displayEl = this.el.select('.x-paging-info', true).first();
28645 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28646 // this.displayEl = navel.el.select('span',true).first();
28652 Roo.each(_this.buttons, function(e){ // this might need to use render????
28653 Roo.factory(e).render(_this.el);
28657 Roo.each(_this.toolbarItems, function(e) {
28658 _this.navgroup.addItem(e);
28662 this.first = this.navgroup.addItem({
28663 tooltip: this.firstText,
28664 cls: "prev btn-outline-secondary",
28665 html : ' <i class="fa fa-step-backward"></i>',
28667 preventDefault: true,
28668 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28671 this.prev = this.navgroup.addItem({
28672 tooltip: this.prevText,
28673 cls: "prev btn-outline-secondary",
28674 html : ' <i class="fa fa-backward"></i>',
28676 preventDefault: true,
28677 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28679 //this.addSeparator();
28682 var field = this.navgroup.addItem( {
28684 cls : 'x-paging-position btn-outline-secondary',
28686 html : this.beforePageText +
28687 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28688 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28691 this.field = field.el.select('input', true).first();
28692 this.field.on("keydown", this.onPagingKeydown, this);
28693 this.field.on("focus", function(){this.dom.select();});
28696 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28697 //this.field.setHeight(18);
28698 //this.addSeparator();
28699 this.next = this.navgroup.addItem({
28700 tooltip: this.nextText,
28701 cls: "next btn-outline-secondary",
28702 html : ' <i class="fa fa-forward"></i>',
28704 preventDefault: true,
28705 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28707 this.last = this.navgroup.addItem({
28708 tooltip: this.lastText,
28709 html : ' <i class="fa fa-step-forward"></i>',
28710 cls: "next btn-outline-secondary",
28712 preventDefault: true,
28713 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28715 //this.addSeparator();
28716 this.loading = this.navgroup.addItem({
28717 tooltip: this.refreshText,
28718 cls: "btn-outline-secondary",
28719 html : ' <i class="fa fa-refresh"></i>',
28720 preventDefault: true,
28721 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28727 updateInfo : function(){
28728 if(this.displayEl){
28729 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28730 var msg = count == 0 ?
28734 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28736 this.displayEl.update(msg);
28741 onLoad : function(ds, r, o)
28743 this.cursor = o.params && o.params.start ? o.params.start : 0;
28745 var d = this.getPageData(),
28750 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28751 this.field.dom.value = ap;
28752 this.first.setDisabled(ap == 1);
28753 this.prev.setDisabled(ap == 1);
28754 this.next.setDisabled(ap == ps);
28755 this.last.setDisabled(ap == ps);
28756 this.loading.enable();
28761 getPageData : function(){
28762 var total = this.ds.getTotalCount();
28765 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28766 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28771 onLoadError : function(){
28772 this.loading.enable();
28776 onPagingKeydown : function(e){
28777 var k = e.getKey();
28778 var d = this.getPageData();
28780 var v = this.field.dom.value, pageNum;
28781 if(!v || isNaN(pageNum = parseInt(v, 10))){
28782 this.field.dom.value = d.activePage;
28785 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28786 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28789 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))
28791 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28792 this.field.dom.value = pageNum;
28793 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28796 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28798 var v = this.field.dom.value, pageNum;
28799 var increment = (e.shiftKey) ? 10 : 1;
28800 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28803 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28804 this.field.dom.value = d.activePage;
28807 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28809 this.field.dom.value = parseInt(v, 10) + increment;
28810 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28811 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28818 beforeLoad : function(){
28820 this.loading.disable();
28825 onClick : function(which){
28834 ds.load({params:{start: 0, limit: this.pageSize}});
28837 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28840 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28843 var total = ds.getTotalCount();
28844 var extra = total % this.pageSize;
28845 var lastStart = extra ? (total - extra) : total-this.pageSize;
28846 ds.load({params:{start: lastStart, limit: this.pageSize}});
28849 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28855 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28856 * @param {Roo.data.Store} store The data store to unbind
28858 unbind : function(ds){
28859 ds.un("beforeload", this.beforeLoad, this);
28860 ds.un("load", this.onLoad, this);
28861 ds.un("loadexception", this.onLoadError, this);
28862 ds.un("remove", this.updateInfo, this);
28863 ds.un("add", this.updateInfo, this);
28864 this.ds = undefined;
28868 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28869 * @param {Roo.data.Store} store The data store to bind
28871 bind : function(ds){
28872 ds.on("beforeload", this.beforeLoad, this);
28873 ds.on("load", this.onLoad, this);
28874 ds.on("loadexception", this.onLoadError, this);
28875 ds.on("remove", this.updateInfo, this);
28876 ds.on("add", this.updateInfo, this);
28887 * @class Roo.bootstrap.MessageBar
28888 * @extends Roo.bootstrap.Component
28889 * Bootstrap MessageBar class
28890 * @cfg {String} html contents of the MessageBar
28891 * @cfg {String} weight (info | success | warning | danger) default info
28892 * @cfg {String} beforeClass insert the bar before the given class
28893 * @cfg {Boolean} closable (true | false) default false
28894 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28897 * Create a new Element
28898 * @param {Object} config The config object
28901 Roo.bootstrap.MessageBar = function(config){
28902 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28905 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28911 beforeClass: 'bootstrap-sticky-wrap',
28913 getAutoCreate : function(){
28917 cls: 'alert alert-dismissable alert-' + this.weight,
28922 html: this.html || ''
28928 cfg.cls += ' alert-messages-fixed';
28942 onRender : function(ct, position)
28944 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28947 var cfg = Roo.apply({}, this.getAutoCreate());
28951 cfg.cls += ' ' + this.cls;
28954 cfg.style = this.style;
28956 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28958 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28961 this.el.select('>button.close').on('click', this.hide, this);
28967 if (!this.rendered) {
28973 this.fireEvent('show', this);
28979 if (!this.rendered) {
28985 this.fireEvent('hide', this);
28988 update : function()
28990 // var e = this.el.dom.firstChild;
28992 // if(this.closable){
28993 // e = e.nextSibling;
28996 // e.data = this.html || '';
28998 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29014 * @class Roo.bootstrap.Graph
29015 * @extends Roo.bootstrap.Component
29016 * Bootstrap Graph class
29020 @cfg {String} graphtype bar | vbar | pie
29021 @cfg {number} g_x coodinator | centre x (pie)
29022 @cfg {number} g_y coodinator | centre y (pie)
29023 @cfg {number} g_r radius (pie)
29024 @cfg {number} g_height height of the chart (respected by all elements in the set)
29025 @cfg {number} g_width width of the chart (respected by all elements in the set)
29026 @cfg {Object} title The title of the chart
29029 -opts (object) options for the chart
29031 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29032 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29034 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.
29035 o stacked (boolean) whether or not to tread values as in a stacked bar chart
29037 o stretch (boolean)
29039 -opts (object) options for the pie
29042 o startAngle (number)
29043 o endAngle (number)
29047 * Create a new Input
29048 * @param {Object} config The config object
29051 Roo.bootstrap.Graph = function(config){
29052 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29058 * The img click event for the img.
29059 * @param {Roo.EventObject} e
29065 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
29076 //g_colors: this.colors,
29083 getAutoCreate : function(){
29094 onRender : function(ct,position){
29097 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29099 if (typeof(Raphael) == 'undefined') {
29100 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29104 this.raphael = Raphael(this.el.dom);
29106 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29107 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29108 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29109 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29111 r.text(160, 10, "Single Series Chart").attr(txtattr);
29112 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29113 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29114 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29116 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29117 r.barchart(330, 10, 300, 220, data1);
29118 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29119 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29122 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29123 // r.barchart(30, 30, 560, 250, xdata, {
29124 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29125 // axis : "0 0 1 1",
29126 // axisxlabels : xdata
29127 // //yvalues : cols,
29130 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29132 // this.load(null,xdata,{
29133 // axis : "0 0 1 1",
29134 // axisxlabels : xdata
29139 load : function(graphtype,xdata,opts)
29141 this.raphael.clear();
29143 graphtype = this.graphtype;
29148 var r = this.raphael,
29149 fin = function () {
29150 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29152 fout = function () {
29153 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29155 pfin = function() {
29156 this.sector.stop();
29157 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29160 this.label[0].stop();
29161 this.label[0].attr({ r: 7.5 });
29162 this.label[1].attr({ "font-weight": 800 });
29165 pfout = function() {
29166 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29169 this.label[0].animate({ r: 5 }, 500, "bounce");
29170 this.label[1].attr({ "font-weight": 400 });
29176 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29179 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29182 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
29183 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29185 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29192 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29197 setTitle: function(o)
29202 initEvents: function() {
29205 this.el.on('click', this.onClick, this);
29209 onClick : function(e)
29211 Roo.log('img onclick');
29212 this.fireEvent('click', this, e);
29224 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29227 * @class Roo.bootstrap.dash.NumberBox
29228 * @extends Roo.bootstrap.Component
29229 * Bootstrap NumberBox class
29230 * @cfg {String} headline Box headline
29231 * @cfg {String} content Box content
29232 * @cfg {String} icon Box icon
29233 * @cfg {String} footer Footer text
29234 * @cfg {String} fhref Footer href
29237 * Create a new NumberBox
29238 * @param {Object} config The config object
29242 Roo.bootstrap.dash.NumberBox = function(config){
29243 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29247 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
29256 getAutoCreate : function(){
29260 cls : 'small-box ',
29268 cls : 'roo-headline',
29269 html : this.headline
29273 cls : 'roo-content',
29274 html : this.content
29288 cls : 'ion ' + this.icon
29297 cls : 'small-box-footer',
29298 href : this.fhref || '#',
29302 cfg.cn.push(footer);
29309 onRender : function(ct,position){
29310 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29317 setHeadline: function (value)
29319 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29322 setFooter: function (value, href)
29324 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29327 this.el.select('a.small-box-footer',true).first().attr('href', href);
29332 setContent: function (value)
29334 this.el.select('.roo-content',true).first().dom.innerHTML = value;
29337 initEvents: function()
29351 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29354 * @class Roo.bootstrap.dash.TabBox
29355 * @extends Roo.bootstrap.Component
29356 * @children Roo.bootstrap.dash.TabPane
29357 * Bootstrap TabBox class
29358 * @cfg {String} title Title of the TabBox
29359 * @cfg {String} icon Icon of the TabBox
29360 * @cfg {Boolean} showtabs (true|false) show the tabs default true
29361 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29364 * Create a new TabBox
29365 * @param {Object} config The config object
29369 Roo.bootstrap.dash.TabBox = function(config){
29370 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29375 * When a pane is added
29376 * @param {Roo.bootstrap.dash.TabPane} pane
29380 * @event activatepane
29381 * When a pane is activated
29382 * @param {Roo.bootstrap.dash.TabPane} pane
29384 "activatepane" : true
29392 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
29397 tabScrollable : false,
29399 getChildContainer : function()
29401 return this.el.select('.tab-content', true).first();
29404 getAutoCreate : function(){
29408 cls: 'pull-left header',
29416 cls: 'fa ' + this.icon
29422 cls: 'nav nav-tabs pull-right',
29428 if(this.tabScrollable){
29435 cls: 'nav nav-tabs pull-right',
29446 cls: 'nav-tabs-custom',
29451 cls: 'tab-content no-padding',
29459 initEvents : function()
29461 //Roo.log('add add pane handler');
29462 this.on('addpane', this.onAddPane, this);
29465 * Updates the box title
29466 * @param {String} html to set the title to.
29468 setTitle : function(value)
29470 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29472 onAddPane : function(pane)
29474 this.panes.push(pane);
29475 //Roo.log('addpane');
29477 // tabs are rendere left to right..
29478 if(!this.showtabs){
29482 var ctr = this.el.select('.nav-tabs', true).first();
29485 var existing = ctr.select('.nav-tab',true);
29486 var qty = existing.getCount();;
29489 var tab = ctr.createChild({
29491 cls : 'nav-tab' + (qty ? '' : ' active'),
29499 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29502 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29504 pane.el.addClass('active');
29509 onTabClick : function(ev,un,ob,pane)
29511 //Roo.log('tab - prev default');
29512 ev.preventDefault();
29515 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29516 pane.tab.addClass('active');
29517 //Roo.log(pane.title);
29518 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29519 // technically we should have a deactivate event.. but maybe add later.
29520 // and it should not de-activate the selected tab...
29521 this.fireEvent('activatepane', pane);
29522 pane.el.addClass('active');
29523 pane.fireEvent('activate');
29528 getActivePane : function()
29531 Roo.each(this.panes, function(p) {
29532 if(p.el.hasClass('active')){
29553 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29555 * @class Roo.bootstrap.TabPane
29556 * @extends Roo.bootstrap.Component
29557 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
29558 * Bootstrap TabPane class
29559 * @cfg {Boolean} active (false | true) Default false
29560 * @cfg {String} title title of panel
29564 * Create a new TabPane
29565 * @param {Object} config The config object
29568 Roo.bootstrap.dash.TabPane = function(config){
29569 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29575 * When a pane is activated
29576 * @param {Roo.bootstrap.dash.TabPane} pane
29583 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29588 // the tabBox that this is attached to.
29591 getAutoCreate : function()
29599 cfg.cls += ' active';
29604 initEvents : function()
29606 //Roo.log('trigger add pane handler');
29607 this.parent().fireEvent('addpane', this)
29611 * Updates the tab title
29612 * @param {String} html to set the title to.
29614 setTitle: function(str)
29620 this.tab.select('a', true).first().dom.innerHTML = str;
29639 * @class Roo.bootstrap.Tooltip
29640 * Bootstrap Tooltip class
29641 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29642 * to determine which dom element triggers the tooltip.
29644 * It needs to add support for additional attributes like tooltip-position
29647 * Create a new Toolti
29648 * @param {Object} config The config object
29651 Roo.bootstrap.Tooltip = function(config){
29652 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29654 this.alignment = Roo.bootstrap.Tooltip.alignment;
29656 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29657 this.alignment = config.alignment;
29662 Roo.apply(Roo.bootstrap.Tooltip, {
29664 * @function init initialize tooltip monitoring.
29668 currentTip : false,
29669 currentRegion : false,
29675 Roo.get(document).on('mouseover', this.enter ,this);
29676 Roo.get(document).on('mouseout', this.leave, this);
29679 this.currentTip = new Roo.bootstrap.Tooltip();
29682 enter : function(ev)
29684 var dom = ev.getTarget();
29686 //Roo.log(['enter',dom]);
29687 var el = Roo.fly(dom);
29688 if (this.currentEl) {
29690 //Roo.log(this.currentEl);
29691 //Roo.log(this.currentEl.contains(dom));
29692 if (this.currentEl == el) {
29695 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29701 if (this.currentTip.el) {
29702 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29706 if(!el || el.dom == document){
29712 if (!el.attr('tooltip')) {
29713 pel = el.findParent("[tooltip]");
29715 bindEl = Roo.get(pel);
29721 // you can not look for children, as if el is the body.. then everythign is the child..
29722 if (!pel && !el.attr('tooltip')) { //
29723 if (!el.select("[tooltip]").elements.length) {
29726 // is the mouse over this child...?
29727 bindEl = el.select("[tooltip]").first();
29728 var xy = ev.getXY();
29729 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29730 //Roo.log("not in region.");
29733 //Roo.log("child element over..");
29736 this.currentEl = el;
29737 this.currentTip.bind(bindEl);
29738 this.currentRegion = Roo.lib.Region.getRegion(dom);
29739 this.currentTip.enter();
29742 leave : function(ev)
29744 var dom = ev.getTarget();
29745 //Roo.log(['leave',dom]);
29746 if (!this.currentEl) {
29751 if (dom != this.currentEl.dom) {
29754 var xy = ev.getXY();
29755 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29758 // only activate leave if mouse cursor is outside... bounding box..
29763 if (this.currentTip) {
29764 this.currentTip.leave();
29766 //Roo.log('clear currentEl');
29767 this.currentEl = false;
29772 'left' : ['r-l', [-2,0], 'right'],
29773 'right' : ['l-r', [2,0], 'left'],
29774 'bottom' : ['t-b', [0,2], 'top'],
29775 'top' : [ 'b-t', [0,-2], 'bottom']
29781 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29786 delay : null, // can be { show : 300 , hide: 500}
29790 hoverState : null, //???
29792 placement : 'bottom',
29796 getAutoCreate : function(){
29803 cls : 'tooltip-arrow arrow'
29806 cls : 'tooltip-inner'
29813 bind : function(el)
29818 initEvents : function()
29820 this.arrowEl = this.el.select('.arrow', true).first();
29821 this.innerEl = this.el.select('.tooltip-inner', true).first();
29824 enter : function () {
29826 if (this.timeout != null) {
29827 clearTimeout(this.timeout);
29830 this.hoverState = 'in';
29831 //Roo.log("enter - show");
29832 if (!this.delay || !this.delay.show) {
29837 this.timeout = setTimeout(function () {
29838 if (_t.hoverState == 'in') {
29841 }, this.delay.show);
29845 clearTimeout(this.timeout);
29847 this.hoverState = 'out';
29848 if (!this.delay || !this.delay.hide) {
29854 this.timeout = setTimeout(function () {
29855 //Roo.log("leave - timeout");
29857 if (_t.hoverState == 'out') {
29859 Roo.bootstrap.Tooltip.currentEl = false;
29864 show : function (msg)
29867 this.render(document.body);
29870 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29872 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29874 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29876 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29877 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29879 var placement = typeof this.placement == 'function' ?
29880 this.placement.call(this, this.el, on_el) :
29883 var autoToken = /\s?auto?\s?/i;
29884 var autoPlace = autoToken.test(placement);
29886 placement = placement.replace(autoToken, '') || 'top';
29890 //this.el.setXY([0,0]);
29892 //this.el.dom.style.display='block';
29894 //this.el.appendTo(on_el);
29896 var p = this.getPosition();
29897 var box = this.el.getBox();
29903 var align = this.alignment[placement];
29905 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29907 if(placement == 'top' || placement == 'bottom'){
29909 placement = 'right';
29912 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29913 placement = 'left';
29916 var scroll = Roo.select('body', true).first().getScroll();
29918 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29922 align = this.alignment[placement];
29924 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29928 var elems = document.getElementsByTagName('div');
29929 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29930 for (var i = 0; i < elems.length; i++) {
29931 var zindex = Number.parseInt(
29932 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29935 if (zindex > highest) {
29942 this.el.dom.style.zIndex = highest;
29944 this.el.alignTo(this.bindEl, align[0],align[1]);
29945 //var arrow = this.el.select('.arrow',true).first();
29946 //arrow.set(align[2],
29948 this.el.addClass(placement);
29949 this.el.addClass("bs-tooltip-"+ placement);
29951 this.el.addClass('in fade show');
29953 this.hoverState = null;
29955 if (this.el.hasClass('fade')) {
29970 //this.el.setXY([0,0]);
29971 this.el.removeClass(['show', 'in']);
29987 * @class Roo.bootstrap.LocationPicker
29988 * @extends Roo.bootstrap.Component
29989 * Bootstrap LocationPicker class
29990 * @cfg {Number} latitude Position when init default 0
29991 * @cfg {Number} longitude Position when init default 0
29992 * @cfg {Number} zoom default 15
29993 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29994 * @cfg {Boolean} mapTypeControl default false
29995 * @cfg {Boolean} disableDoubleClickZoom default false
29996 * @cfg {Boolean} scrollwheel default true
29997 * @cfg {Boolean} streetViewControl default false
29998 * @cfg {Number} radius default 0
29999 * @cfg {String} locationName
30000 * @cfg {Boolean} draggable default true
30001 * @cfg {Boolean} enableAutocomplete default false
30002 * @cfg {Boolean} enableReverseGeocode default true
30003 * @cfg {String} markerTitle
30006 * Create a new LocationPicker
30007 * @param {Object} config The config object
30011 Roo.bootstrap.LocationPicker = function(config){
30013 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30018 * Fires when the picker initialized.
30019 * @param {Roo.bootstrap.LocationPicker} this
30020 * @param {Google Location} location
30024 * @event positionchanged
30025 * Fires when the picker position changed.
30026 * @param {Roo.bootstrap.LocationPicker} this
30027 * @param {Google Location} location
30029 positionchanged : true,
30032 * Fires when the map resize.
30033 * @param {Roo.bootstrap.LocationPicker} this
30038 * Fires when the map show.
30039 * @param {Roo.bootstrap.LocationPicker} this
30044 * Fires when the map hide.
30045 * @param {Roo.bootstrap.LocationPicker} this
30050 * Fires when click the map.
30051 * @param {Roo.bootstrap.LocationPicker} this
30052 * @param {Map event} e
30056 * @event mapRightClick
30057 * Fires when right click the map.
30058 * @param {Roo.bootstrap.LocationPicker} this
30059 * @param {Map event} e
30061 mapRightClick : true,
30063 * @event markerClick
30064 * Fires when click the marker.
30065 * @param {Roo.bootstrap.LocationPicker} this
30066 * @param {Map event} e
30068 markerClick : true,
30070 * @event markerRightClick
30071 * Fires when right click the marker.
30072 * @param {Roo.bootstrap.LocationPicker} this
30073 * @param {Map event} e
30075 markerRightClick : true,
30077 * @event OverlayViewDraw
30078 * Fires when OverlayView Draw
30079 * @param {Roo.bootstrap.LocationPicker} this
30081 OverlayViewDraw : true,
30083 * @event OverlayViewOnAdd
30084 * Fires when OverlayView Draw
30085 * @param {Roo.bootstrap.LocationPicker} this
30087 OverlayViewOnAdd : true,
30089 * @event OverlayViewOnRemove
30090 * Fires when OverlayView Draw
30091 * @param {Roo.bootstrap.LocationPicker} this
30093 OverlayViewOnRemove : true,
30095 * @event OverlayViewShow
30096 * Fires when OverlayView Draw
30097 * @param {Roo.bootstrap.LocationPicker} this
30098 * @param {Pixel} cpx
30100 OverlayViewShow : true,
30102 * @event OverlayViewHide
30103 * Fires when OverlayView Draw
30104 * @param {Roo.bootstrap.LocationPicker} this
30106 OverlayViewHide : true,
30108 * @event loadexception
30109 * Fires when load google lib failed.
30110 * @param {Roo.bootstrap.LocationPicker} this
30112 loadexception : true
30117 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30119 gMapContext: false,
30125 mapTypeControl: false,
30126 disableDoubleClickZoom: false,
30128 streetViewControl: false,
30132 enableAutocomplete: false,
30133 enableReverseGeocode: true,
30136 getAutoCreate: function()
30141 cls: 'roo-location-picker'
30147 initEvents: function(ct, position)
30149 if(!this.el.getWidth() || this.isApplied()){
30153 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30158 initial: function()
30160 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30161 this.fireEvent('loadexception', this);
30165 if(!this.mapTypeId){
30166 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30169 this.gMapContext = this.GMapContext();
30171 this.initOverlayView();
30173 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30177 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30178 _this.setPosition(_this.gMapContext.marker.position);
30181 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30182 _this.fireEvent('mapClick', this, event);
30186 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30187 _this.fireEvent('mapRightClick', this, event);
30191 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30192 _this.fireEvent('markerClick', this, event);
30196 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30197 _this.fireEvent('markerRightClick', this, event);
30201 this.setPosition(this.gMapContext.location);
30203 this.fireEvent('initial', this, this.gMapContext.location);
30206 initOverlayView: function()
30210 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30214 _this.fireEvent('OverlayViewDraw', _this);
30219 _this.fireEvent('OverlayViewOnAdd', _this);
30222 onRemove: function()
30224 _this.fireEvent('OverlayViewOnRemove', _this);
30227 show: function(cpx)
30229 _this.fireEvent('OverlayViewShow', _this, cpx);
30234 _this.fireEvent('OverlayViewHide', _this);
30240 fromLatLngToContainerPixel: function(event)
30242 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30245 isApplied: function()
30247 return this.getGmapContext() == false ? false : true;
30250 getGmapContext: function()
30252 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30255 GMapContext: function()
30257 var position = new google.maps.LatLng(this.latitude, this.longitude);
30259 var _map = new google.maps.Map(this.el.dom, {
30262 mapTypeId: this.mapTypeId,
30263 mapTypeControl: this.mapTypeControl,
30264 disableDoubleClickZoom: this.disableDoubleClickZoom,
30265 scrollwheel: this.scrollwheel,
30266 streetViewControl: this.streetViewControl,
30267 locationName: this.locationName,
30268 draggable: this.draggable,
30269 enableAutocomplete: this.enableAutocomplete,
30270 enableReverseGeocode: this.enableReverseGeocode
30273 var _marker = new google.maps.Marker({
30274 position: position,
30276 title: this.markerTitle,
30277 draggable: this.draggable
30284 location: position,
30285 radius: this.radius,
30286 locationName: this.locationName,
30287 addressComponents: {
30288 formatted_address: null,
30289 addressLine1: null,
30290 addressLine2: null,
30292 streetNumber: null,
30296 stateOrProvince: null
30299 domContainer: this.el.dom,
30300 geodecoder: new google.maps.Geocoder()
30304 drawCircle: function(center, radius, options)
30306 if (this.gMapContext.circle != null) {
30307 this.gMapContext.circle.setMap(null);
30311 options = Roo.apply({}, options, {
30312 strokeColor: "#0000FF",
30313 strokeOpacity: .35,
30315 fillColor: "#0000FF",
30319 options.map = this.gMapContext.map;
30320 options.radius = radius;
30321 options.center = center;
30322 this.gMapContext.circle = new google.maps.Circle(options);
30323 return this.gMapContext.circle;
30329 setPosition: function(location)
30331 this.gMapContext.location = location;
30332 this.gMapContext.marker.setPosition(location);
30333 this.gMapContext.map.panTo(location);
30334 this.drawCircle(location, this.gMapContext.radius, {});
30338 if (this.gMapContext.settings.enableReverseGeocode) {
30339 this.gMapContext.geodecoder.geocode({
30340 latLng: this.gMapContext.location
30341 }, function(results, status) {
30343 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30344 _this.gMapContext.locationName = results[0].formatted_address;
30345 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30347 _this.fireEvent('positionchanged', this, location);
30354 this.fireEvent('positionchanged', this, location);
30359 google.maps.event.trigger(this.gMapContext.map, "resize");
30361 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30363 this.fireEvent('resize', this);
30366 setPositionByLatLng: function(latitude, longitude)
30368 this.setPosition(new google.maps.LatLng(latitude, longitude));
30371 getCurrentPosition: function()
30374 latitude: this.gMapContext.location.lat(),
30375 longitude: this.gMapContext.location.lng()
30379 getAddressName: function()
30381 return this.gMapContext.locationName;
30384 getAddressComponents: function()
30386 return this.gMapContext.addressComponents;
30389 address_component_from_google_geocode: function(address_components)
30393 for (var i = 0; i < address_components.length; i++) {
30394 var component = address_components[i];
30395 if (component.types.indexOf("postal_code") >= 0) {
30396 result.postalCode = component.short_name;
30397 } else if (component.types.indexOf("street_number") >= 0) {
30398 result.streetNumber = component.short_name;
30399 } else if (component.types.indexOf("route") >= 0) {
30400 result.streetName = component.short_name;
30401 } else if (component.types.indexOf("neighborhood") >= 0) {
30402 result.city = component.short_name;
30403 } else if (component.types.indexOf("locality") >= 0) {
30404 result.city = component.short_name;
30405 } else if (component.types.indexOf("sublocality") >= 0) {
30406 result.district = component.short_name;
30407 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30408 result.stateOrProvince = component.short_name;
30409 } else if (component.types.indexOf("country") >= 0) {
30410 result.country = component.short_name;
30414 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30415 result.addressLine2 = "";
30419 setZoomLevel: function(zoom)
30421 this.gMapContext.map.setZoom(zoom);
30434 this.fireEvent('show', this);
30445 this.fireEvent('hide', this);
30450 Roo.apply(Roo.bootstrap.LocationPicker, {
30452 OverlayView : function(map, options)
30454 options = options || {};
30461 * @class Roo.bootstrap.Alert
30462 * @extends Roo.bootstrap.Component
30463 * Bootstrap Alert class - shows an alert area box
30465 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30466 Enter a valid email address
30469 * @cfg {String} title The title of alert
30470 * @cfg {String} html The content of alert
30471 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30472 * @cfg {String} fa font-awesomeicon
30473 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30474 * @cfg {Boolean} close true to show a x closer
30478 * Create a new alert
30479 * @param {Object} config The config object
30483 Roo.bootstrap.Alert = function(config){
30484 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30488 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30494 faicon: false, // BC
30498 getAutoCreate : function()
30510 style : this.close ? '' : 'display:none'
30514 cls : 'roo-alert-icon'
30519 cls : 'roo-alert-title',
30524 cls : 'roo-alert-text',
30531 cfg.cn[0].cls += ' fa ' + this.faicon;
30534 cfg.cn[0].cls += ' fa ' + this.fa;
30538 cfg.cls += ' alert-' + this.weight;
30544 initEvents: function()
30546 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30547 this.titleEl = this.el.select('.roo-alert-title',true).first();
30548 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30549 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30550 if (this.seconds > 0) {
30551 this.hide.defer(this.seconds, this);
30555 * Set the Title Message HTML
30556 * @param {String} html
30558 setTitle : function(str)
30560 this.titleEl.dom.innerHTML = str;
30564 * Set the Body Message HTML
30565 * @param {String} html
30567 setHtml : function(str)
30569 this.htmlEl.dom.innerHTML = str;
30572 * Set the Weight of the alert
30573 * @param {String} (success|info|warning|danger) weight
30576 setWeight : function(weight)
30579 this.el.removeClass('alert-' + this.weight);
30582 this.weight = weight;
30584 this.el.addClass('alert-' + this.weight);
30587 * Set the Icon of the alert
30588 * @param {String} see fontawsome names (name without the 'fa-' bit)
30590 setIcon : function(icon)
30593 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30596 this.faicon = icon;
30598 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30623 * @class Roo.bootstrap.UploadCropbox
30624 * @extends Roo.bootstrap.Component
30625 * Bootstrap UploadCropbox class
30626 * @cfg {String} emptyText show when image has been loaded
30627 * @cfg {String} rotateNotify show when image too small to rotate
30628 * @cfg {Number} errorTimeout default 3000
30629 * @cfg {Number} minWidth default 300
30630 * @cfg {Number} minHeight default 300
30631 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30632 * @cfg {Boolean} isDocument (true|false) default false
30633 * @cfg {String} url action url
30634 * @cfg {String} paramName default 'imageUpload'
30635 * @cfg {String} method default POST
30636 * @cfg {Boolean} loadMask (true|false) default true
30637 * @cfg {Boolean} loadingText default 'Loading...'
30640 * Create a new UploadCropbox
30641 * @param {Object} config The config object
30644 Roo.bootstrap.UploadCropbox = function(config){
30645 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30649 * @event beforeselectfile
30650 * Fire before select file
30651 * @param {Roo.bootstrap.UploadCropbox} this
30653 "beforeselectfile" : true,
30656 * Fire after initEvent
30657 * @param {Roo.bootstrap.UploadCropbox} this
30662 * Fire after initEvent
30663 * @param {Roo.bootstrap.UploadCropbox} this
30664 * @param {String} data
30669 * Fire when preparing the file data
30670 * @param {Roo.bootstrap.UploadCropbox} this
30671 * @param {Object} file
30676 * Fire when get exception
30677 * @param {Roo.bootstrap.UploadCropbox} this
30678 * @param {XMLHttpRequest} xhr
30680 "exception" : true,
30682 * @event beforeloadcanvas
30683 * Fire before load the canvas
30684 * @param {Roo.bootstrap.UploadCropbox} this
30685 * @param {String} src
30687 "beforeloadcanvas" : true,
30690 * Fire when trash image
30691 * @param {Roo.bootstrap.UploadCropbox} this
30696 * Fire when download the image
30697 * @param {Roo.bootstrap.UploadCropbox} this
30701 * @event footerbuttonclick
30702 * Fire when footerbuttonclick
30703 * @param {Roo.bootstrap.UploadCropbox} this
30704 * @param {String} type
30706 "footerbuttonclick" : true,
30710 * @param {Roo.bootstrap.UploadCropbox} this
30715 * Fire when rotate the image
30716 * @param {Roo.bootstrap.UploadCropbox} this
30717 * @param {String} pos
30722 * Fire when inspect the file
30723 * @param {Roo.bootstrap.UploadCropbox} this
30724 * @param {Object} file
30729 * Fire when xhr upload the file
30730 * @param {Roo.bootstrap.UploadCropbox} this
30731 * @param {Object} data
30736 * Fire when arrange the file data
30737 * @param {Roo.bootstrap.UploadCropbox} this
30738 * @param {Object} formData
30743 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30746 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30748 emptyText : 'Click to upload image',
30749 rotateNotify : 'Image is too small to rotate',
30750 errorTimeout : 3000,
30764 cropType : 'image/jpeg',
30766 canvasLoaded : false,
30767 isDocument : false,
30769 paramName : 'imageUpload',
30771 loadingText : 'Loading...',
30774 getAutoCreate : function()
30778 cls : 'roo-upload-cropbox',
30782 cls : 'roo-upload-cropbox-selector',
30787 cls : 'roo-upload-cropbox-body',
30788 style : 'cursor:pointer',
30792 cls : 'roo-upload-cropbox-preview'
30796 cls : 'roo-upload-cropbox-thumb'
30800 cls : 'roo-upload-cropbox-empty-notify',
30801 html : this.emptyText
30805 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30806 html : this.rotateNotify
30812 cls : 'roo-upload-cropbox-footer',
30815 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30825 onRender : function(ct, position)
30827 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30829 if (this.buttons.length) {
30831 Roo.each(this.buttons, function(bb) {
30833 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30835 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30841 this.maskEl = this.el;
30845 initEvents : function()
30847 this.urlAPI = (window.createObjectURL && window) ||
30848 (window.URL && URL.revokeObjectURL && URL) ||
30849 (window.webkitURL && webkitURL);
30851 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30852 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30854 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30855 this.selectorEl.hide();
30857 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30858 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30860 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30861 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30862 this.thumbEl.hide();
30864 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30865 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30867 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30868 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30869 this.errorEl.hide();
30871 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30872 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30873 this.footerEl.hide();
30875 this.setThumbBoxSize();
30881 this.fireEvent('initial', this);
30888 window.addEventListener("resize", function() { _this.resize(); } );
30890 this.bodyEl.on('click', this.beforeSelectFile, this);
30893 this.bodyEl.on('touchstart', this.onTouchStart, this);
30894 this.bodyEl.on('touchmove', this.onTouchMove, this);
30895 this.bodyEl.on('touchend', this.onTouchEnd, this);
30899 this.bodyEl.on('mousedown', this.onMouseDown, this);
30900 this.bodyEl.on('mousemove', this.onMouseMove, this);
30901 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30902 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30903 Roo.get(document).on('mouseup', this.onMouseUp, this);
30906 this.selectorEl.on('change', this.onFileSelected, this);
30912 this.baseScale = 1;
30914 this.baseRotate = 1;
30915 this.dragable = false;
30916 this.pinching = false;
30919 this.cropData = false;
30920 this.notifyEl.dom.innerHTML = this.emptyText;
30922 this.selectorEl.dom.value = '';
30926 resize : function()
30928 if(this.fireEvent('resize', this) != false){
30929 this.setThumbBoxPosition();
30930 this.setCanvasPosition();
30934 onFooterButtonClick : function(e, el, o, type)
30937 case 'rotate-left' :
30938 this.onRotateLeft(e);
30940 case 'rotate-right' :
30941 this.onRotateRight(e);
30944 this.beforeSelectFile(e);
30959 this.fireEvent('footerbuttonclick', this, type);
30962 beforeSelectFile : function(e)
30964 e.preventDefault();
30966 if(this.fireEvent('beforeselectfile', this) != false){
30967 this.selectorEl.dom.click();
30971 onFileSelected : function(e)
30973 e.preventDefault();
30975 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30979 var file = this.selectorEl.dom.files[0];
30981 if(this.fireEvent('inspect', this, file) != false){
30982 this.prepare(file);
30987 trash : function(e)
30989 this.fireEvent('trash', this);
30992 download : function(e)
30994 this.fireEvent('download', this);
30997 loadCanvas : function(src)
30999 if(this.fireEvent('beforeloadcanvas', this, src) != false){
31003 this.imageEl = document.createElement('img');
31007 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31009 this.imageEl.src = src;
31013 onLoadCanvas : function()
31015 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31016 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31018 this.bodyEl.un('click', this.beforeSelectFile, this);
31020 this.notifyEl.hide();
31021 this.thumbEl.show();
31022 this.footerEl.show();
31024 this.baseRotateLevel();
31026 if(this.isDocument){
31027 this.setThumbBoxSize();
31030 this.setThumbBoxPosition();
31032 this.baseScaleLevel();
31038 this.canvasLoaded = true;
31041 this.maskEl.unmask();
31046 setCanvasPosition : function()
31048 if(!this.canvasEl){
31052 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31053 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31055 this.previewEl.setLeft(pw);
31056 this.previewEl.setTop(ph);
31060 onMouseDown : function(e)
31064 this.dragable = true;
31065 this.pinching = false;
31067 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31068 this.dragable = false;
31072 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31073 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31077 onMouseMove : function(e)
31081 if(!this.canvasLoaded){
31085 if (!this.dragable){
31089 var minX = Math.ceil(this.thumbEl.getLeft(true));
31090 var minY = Math.ceil(this.thumbEl.getTop(true));
31092 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31093 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31095 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31096 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31098 x = x - this.mouseX;
31099 y = y - this.mouseY;
31101 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31102 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31104 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31105 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31107 this.previewEl.setLeft(bgX);
31108 this.previewEl.setTop(bgY);
31110 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31111 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31114 onMouseUp : function(e)
31118 this.dragable = false;
31121 onMouseWheel : function(e)
31125 this.startScale = this.scale;
31127 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31129 if(!this.zoomable()){
31130 this.scale = this.startScale;
31139 zoomable : function()
31141 var minScale = this.thumbEl.getWidth() / this.minWidth;
31143 if(this.minWidth < this.minHeight){
31144 minScale = this.thumbEl.getHeight() / this.minHeight;
31147 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31148 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31152 (this.rotate == 0 || this.rotate == 180) &&
31154 width > this.imageEl.OriginWidth ||
31155 height > this.imageEl.OriginHeight ||
31156 (width < this.minWidth && height < this.minHeight)
31164 (this.rotate == 90 || this.rotate == 270) &&
31166 width > this.imageEl.OriginWidth ||
31167 height > this.imageEl.OriginHeight ||
31168 (width < this.minHeight && height < this.minWidth)
31175 !this.isDocument &&
31176 (this.rotate == 0 || this.rotate == 180) &&
31178 width < this.minWidth ||
31179 width > this.imageEl.OriginWidth ||
31180 height < this.minHeight ||
31181 height > this.imageEl.OriginHeight
31188 !this.isDocument &&
31189 (this.rotate == 90 || this.rotate == 270) &&
31191 width < this.minHeight ||
31192 width > this.imageEl.OriginWidth ||
31193 height < this.minWidth ||
31194 height > this.imageEl.OriginHeight
31204 onRotateLeft : function(e)
31206 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31208 var minScale = this.thumbEl.getWidth() / this.minWidth;
31210 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31211 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31213 this.startScale = this.scale;
31215 while (this.getScaleLevel() < minScale){
31217 this.scale = this.scale + 1;
31219 if(!this.zoomable()){
31224 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31225 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31230 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31237 this.scale = this.startScale;
31239 this.onRotateFail();
31244 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31246 if(this.isDocument){
31247 this.setThumbBoxSize();
31248 this.setThumbBoxPosition();
31249 this.setCanvasPosition();
31254 this.fireEvent('rotate', this, 'left');
31258 onRotateRight : function(e)
31260 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31262 var minScale = this.thumbEl.getWidth() / this.minWidth;
31264 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31265 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31267 this.startScale = this.scale;
31269 while (this.getScaleLevel() < minScale){
31271 this.scale = this.scale + 1;
31273 if(!this.zoomable()){
31278 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31279 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31284 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31291 this.scale = this.startScale;
31293 this.onRotateFail();
31298 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31300 if(this.isDocument){
31301 this.setThumbBoxSize();
31302 this.setThumbBoxPosition();
31303 this.setCanvasPosition();
31308 this.fireEvent('rotate', this, 'right');
31311 onRotateFail : function()
31313 this.errorEl.show(true);
31317 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31322 this.previewEl.dom.innerHTML = '';
31324 var canvasEl = document.createElement("canvas");
31326 var contextEl = canvasEl.getContext("2d");
31328 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31329 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31330 var center = this.imageEl.OriginWidth / 2;
31332 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31333 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31334 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31335 center = this.imageEl.OriginHeight / 2;
31338 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31340 contextEl.translate(center, center);
31341 contextEl.rotate(this.rotate * Math.PI / 180);
31343 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31345 this.canvasEl = document.createElement("canvas");
31347 this.contextEl = this.canvasEl.getContext("2d");
31349 switch (this.rotate) {
31352 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31353 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31355 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31360 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31361 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31363 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31364 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);
31368 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31373 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31374 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31376 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31377 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);
31381 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);
31386 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31387 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31389 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31390 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31394 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);
31401 this.previewEl.appendChild(this.canvasEl);
31403 this.setCanvasPosition();
31408 if(!this.canvasLoaded){
31412 var imageCanvas = document.createElement("canvas");
31414 var imageContext = imageCanvas.getContext("2d");
31416 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31417 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31419 var center = imageCanvas.width / 2;
31421 imageContext.translate(center, center);
31423 imageContext.rotate(this.rotate * Math.PI / 180);
31425 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31427 var canvas = document.createElement("canvas");
31429 var context = canvas.getContext("2d");
31431 canvas.width = this.minWidth;
31432 canvas.height = this.minHeight;
31434 switch (this.rotate) {
31437 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31438 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31440 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31441 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31443 var targetWidth = this.minWidth - 2 * x;
31444 var targetHeight = this.minHeight - 2 * y;
31448 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31449 scale = targetWidth / width;
31452 if(x > 0 && y == 0){
31453 scale = targetHeight / height;
31456 if(x > 0 && y > 0){
31457 scale = targetWidth / width;
31459 if(width < height){
31460 scale = targetHeight / height;
31464 context.scale(scale, scale);
31466 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31467 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31469 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31470 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31472 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31477 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31478 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31480 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31481 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31483 var targetWidth = this.minWidth - 2 * x;
31484 var targetHeight = this.minHeight - 2 * y;
31488 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31489 scale = targetWidth / width;
31492 if(x > 0 && y == 0){
31493 scale = targetHeight / height;
31496 if(x > 0 && y > 0){
31497 scale = targetWidth / width;
31499 if(width < height){
31500 scale = targetHeight / height;
31504 context.scale(scale, scale);
31506 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31507 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31509 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31510 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31512 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31514 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31519 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31520 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31522 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31523 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31525 var targetWidth = this.minWidth - 2 * x;
31526 var targetHeight = this.minHeight - 2 * y;
31530 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31531 scale = targetWidth / width;
31534 if(x > 0 && y == 0){
31535 scale = targetHeight / height;
31538 if(x > 0 && y > 0){
31539 scale = targetWidth / width;
31541 if(width < height){
31542 scale = targetHeight / height;
31546 context.scale(scale, scale);
31548 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31549 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31551 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31552 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31554 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31555 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31557 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31562 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31563 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31565 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31566 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31568 var targetWidth = this.minWidth - 2 * x;
31569 var targetHeight = this.minHeight - 2 * y;
31573 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31574 scale = targetWidth / width;
31577 if(x > 0 && y == 0){
31578 scale = targetHeight / height;
31581 if(x > 0 && y > 0){
31582 scale = targetWidth / width;
31584 if(width < height){
31585 scale = targetHeight / height;
31589 context.scale(scale, scale);
31591 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31592 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31594 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31595 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31597 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31599 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31606 this.cropData = canvas.toDataURL(this.cropType);
31608 if(this.fireEvent('crop', this, this.cropData) !== false){
31609 this.process(this.file, this.cropData);
31616 setThumbBoxSize : function()
31620 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31621 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31622 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31624 this.minWidth = width;
31625 this.minHeight = height;
31627 if(this.rotate == 90 || this.rotate == 270){
31628 this.minWidth = height;
31629 this.minHeight = width;
31634 width = Math.ceil(this.minWidth * height / this.minHeight);
31636 if(this.minWidth > this.minHeight){
31638 height = Math.ceil(this.minHeight * width / this.minWidth);
31641 this.thumbEl.setStyle({
31642 width : width + 'px',
31643 height : height + 'px'
31650 setThumbBoxPosition : function()
31652 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31653 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31655 this.thumbEl.setLeft(x);
31656 this.thumbEl.setTop(y);
31660 baseRotateLevel : function()
31662 this.baseRotate = 1;
31665 typeof(this.exif) != 'undefined' &&
31666 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31667 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31669 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31672 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31676 baseScaleLevel : function()
31680 if(this.isDocument){
31682 if(this.baseRotate == 6 || this.baseRotate == 8){
31684 height = this.thumbEl.getHeight();
31685 this.baseScale = height / this.imageEl.OriginWidth;
31687 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31688 width = this.thumbEl.getWidth();
31689 this.baseScale = width / this.imageEl.OriginHeight;
31695 height = this.thumbEl.getHeight();
31696 this.baseScale = height / this.imageEl.OriginHeight;
31698 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31699 width = this.thumbEl.getWidth();
31700 this.baseScale = width / this.imageEl.OriginWidth;
31706 if(this.baseRotate == 6 || this.baseRotate == 8){
31708 width = this.thumbEl.getHeight();
31709 this.baseScale = width / this.imageEl.OriginHeight;
31711 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31712 height = this.thumbEl.getWidth();
31713 this.baseScale = height / this.imageEl.OriginHeight;
31716 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31717 height = this.thumbEl.getWidth();
31718 this.baseScale = height / this.imageEl.OriginHeight;
31720 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31721 width = this.thumbEl.getHeight();
31722 this.baseScale = width / this.imageEl.OriginWidth;
31729 width = this.thumbEl.getWidth();
31730 this.baseScale = width / this.imageEl.OriginWidth;
31732 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31733 height = this.thumbEl.getHeight();
31734 this.baseScale = height / this.imageEl.OriginHeight;
31737 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31739 height = this.thumbEl.getHeight();
31740 this.baseScale = height / this.imageEl.OriginHeight;
31742 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31743 width = this.thumbEl.getWidth();
31744 this.baseScale = width / this.imageEl.OriginWidth;
31752 getScaleLevel : function()
31754 return this.baseScale * Math.pow(1.1, this.scale);
31757 onTouchStart : function(e)
31759 if(!this.canvasLoaded){
31760 this.beforeSelectFile(e);
31764 var touches = e.browserEvent.touches;
31770 if(touches.length == 1){
31771 this.onMouseDown(e);
31775 if(touches.length != 2){
31781 for(var i = 0, finger; finger = touches[i]; i++){
31782 coords.push(finger.pageX, finger.pageY);
31785 var x = Math.pow(coords[0] - coords[2], 2);
31786 var y = Math.pow(coords[1] - coords[3], 2);
31788 this.startDistance = Math.sqrt(x + y);
31790 this.startScale = this.scale;
31792 this.pinching = true;
31793 this.dragable = false;
31797 onTouchMove : function(e)
31799 if(!this.pinching && !this.dragable){
31803 var touches = e.browserEvent.touches;
31810 this.onMouseMove(e);
31816 for(var i = 0, finger; finger = touches[i]; i++){
31817 coords.push(finger.pageX, finger.pageY);
31820 var x = Math.pow(coords[0] - coords[2], 2);
31821 var y = Math.pow(coords[1] - coords[3], 2);
31823 this.endDistance = Math.sqrt(x + y);
31825 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31827 if(!this.zoomable()){
31828 this.scale = this.startScale;
31836 onTouchEnd : function(e)
31838 this.pinching = false;
31839 this.dragable = false;
31843 process : function(file, crop)
31846 this.maskEl.mask(this.loadingText);
31849 this.xhr = new XMLHttpRequest();
31851 file.xhr = this.xhr;
31853 this.xhr.open(this.method, this.url, true);
31856 "Accept": "application/json",
31857 "Cache-Control": "no-cache",
31858 "X-Requested-With": "XMLHttpRequest"
31861 for (var headerName in headers) {
31862 var headerValue = headers[headerName];
31864 this.xhr.setRequestHeader(headerName, headerValue);
31870 this.xhr.onload = function()
31872 _this.xhrOnLoad(_this.xhr);
31875 this.xhr.onerror = function()
31877 _this.xhrOnError(_this.xhr);
31880 var formData = new FormData();
31882 formData.append('returnHTML', 'NO');
31885 formData.append('crop', crop);
31888 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31889 formData.append(this.paramName, file, file.name);
31892 if(typeof(file.filename) != 'undefined'){
31893 formData.append('filename', file.filename);
31896 if(typeof(file.mimetype) != 'undefined'){
31897 formData.append('mimetype', file.mimetype);
31900 if(this.fireEvent('arrange', this, formData) != false){
31901 this.xhr.send(formData);
31905 xhrOnLoad : function(xhr)
31908 this.maskEl.unmask();
31911 if (xhr.readyState !== 4) {
31912 this.fireEvent('exception', this, xhr);
31916 var response = Roo.decode(xhr.responseText);
31918 if(!response.success){
31919 this.fireEvent('exception', this, xhr);
31923 var response = Roo.decode(xhr.responseText);
31925 this.fireEvent('upload', this, response);
31929 xhrOnError : function()
31932 this.maskEl.unmask();
31935 Roo.log('xhr on error');
31937 var response = Roo.decode(xhr.responseText);
31943 prepare : function(file)
31946 this.maskEl.mask(this.loadingText);
31952 if(typeof(file) === 'string'){
31953 this.loadCanvas(file);
31957 if(!file || !this.urlAPI){
31962 this.cropType = file.type;
31966 if(this.fireEvent('prepare', this, this.file) != false){
31968 var reader = new FileReader();
31970 reader.onload = function (e) {
31971 if (e.target.error) {
31972 Roo.log(e.target.error);
31976 var buffer = e.target.result,
31977 dataView = new DataView(buffer),
31979 maxOffset = dataView.byteLength - 4,
31983 if (dataView.getUint16(0) === 0xffd8) {
31984 while (offset < maxOffset) {
31985 markerBytes = dataView.getUint16(offset);
31987 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31988 markerLength = dataView.getUint16(offset + 2) + 2;
31989 if (offset + markerLength > dataView.byteLength) {
31990 Roo.log('Invalid meta data: Invalid segment size.');
31994 if(markerBytes == 0xffe1){
31995 _this.parseExifData(
32002 offset += markerLength;
32012 var url = _this.urlAPI.createObjectURL(_this.file);
32014 _this.loadCanvas(url);
32019 reader.readAsArrayBuffer(this.file);
32025 parseExifData : function(dataView, offset, length)
32027 var tiffOffset = offset + 10,
32031 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32032 // No Exif data, might be XMP data instead
32036 // Check for the ASCII code for "Exif" (0x45786966):
32037 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32038 // No Exif data, might be XMP data instead
32041 if (tiffOffset + 8 > dataView.byteLength) {
32042 Roo.log('Invalid Exif data: Invalid segment size.');
32045 // Check for the two null bytes:
32046 if (dataView.getUint16(offset + 8) !== 0x0000) {
32047 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32050 // Check the byte alignment:
32051 switch (dataView.getUint16(tiffOffset)) {
32053 littleEndian = true;
32056 littleEndian = false;
32059 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32062 // Check for the TIFF tag marker (0x002A):
32063 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32064 Roo.log('Invalid Exif data: Missing TIFF marker.');
32067 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32068 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32070 this.parseExifTags(
32073 tiffOffset + dirOffset,
32078 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32083 if (dirOffset + 6 > dataView.byteLength) {
32084 Roo.log('Invalid Exif data: Invalid directory offset.');
32087 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32088 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32089 if (dirEndOffset + 4 > dataView.byteLength) {
32090 Roo.log('Invalid Exif data: Invalid directory size.');
32093 for (i = 0; i < tagsNumber; i += 1) {
32097 dirOffset + 2 + 12 * i, // tag offset
32101 // Return the offset to the next directory:
32102 return dataView.getUint32(dirEndOffset, littleEndian);
32105 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32107 var tag = dataView.getUint16(offset, littleEndian);
32109 this.exif[tag] = this.getExifValue(
32113 dataView.getUint16(offset + 2, littleEndian), // tag type
32114 dataView.getUint32(offset + 4, littleEndian), // tag length
32119 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32121 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32130 Roo.log('Invalid Exif data: Invalid tag type.');
32134 tagSize = tagType.size * length;
32135 // Determine if the value is contained in the dataOffset bytes,
32136 // or if the value at the dataOffset is a pointer to the actual data:
32137 dataOffset = tagSize > 4 ?
32138 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32139 if (dataOffset + tagSize > dataView.byteLength) {
32140 Roo.log('Invalid Exif data: Invalid data offset.');
32143 if (length === 1) {
32144 return tagType.getValue(dataView, dataOffset, littleEndian);
32147 for (i = 0; i < length; i += 1) {
32148 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32151 if (tagType.ascii) {
32153 // Concatenate the chars:
32154 for (i = 0; i < values.length; i += 1) {
32156 // Ignore the terminating NULL byte(s):
32157 if (c === '\u0000') {
32169 Roo.apply(Roo.bootstrap.UploadCropbox, {
32171 'Orientation': 0x0112
32175 1: 0, //'top-left',
32177 3: 180, //'bottom-right',
32178 // 4: 'bottom-left',
32180 6: 90, //'right-top',
32181 // 7: 'right-bottom',
32182 8: 270 //'left-bottom'
32186 // byte, 8-bit unsigned int:
32188 getValue: function (dataView, dataOffset) {
32189 return dataView.getUint8(dataOffset);
32193 // ascii, 8-bit byte:
32195 getValue: function (dataView, dataOffset) {
32196 return String.fromCharCode(dataView.getUint8(dataOffset));
32201 // short, 16 bit int:
32203 getValue: function (dataView, dataOffset, littleEndian) {
32204 return dataView.getUint16(dataOffset, littleEndian);
32208 // long, 32 bit int:
32210 getValue: function (dataView, dataOffset, littleEndian) {
32211 return dataView.getUint32(dataOffset, littleEndian);
32215 // rational = two long values, first is numerator, second is denominator:
32217 getValue: function (dataView, dataOffset, littleEndian) {
32218 return dataView.getUint32(dataOffset, littleEndian) /
32219 dataView.getUint32(dataOffset + 4, littleEndian);
32223 // slong, 32 bit signed int:
32225 getValue: function (dataView, dataOffset, littleEndian) {
32226 return dataView.getInt32(dataOffset, littleEndian);
32230 // srational, two slongs, first is numerator, second is denominator:
32232 getValue: function (dataView, dataOffset, littleEndian) {
32233 return dataView.getInt32(dataOffset, littleEndian) /
32234 dataView.getInt32(dataOffset + 4, littleEndian);
32244 cls : 'btn-group roo-upload-cropbox-rotate-left',
32245 action : 'rotate-left',
32249 cls : 'btn btn-default',
32250 html : '<i class="fa fa-undo"></i>'
32256 cls : 'btn-group roo-upload-cropbox-picture',
32257 action : 'picture',
32261 cls : 'btn btn-default',
32262 html : '<i class="fa fa-picture-o"></i>'
32268 cls : 'btn-group roo-upload-cropbox-rotate-right',
32269 action : 'rotate-right',
32273 cls : 'btn btn-default',
32274 html : '<i class="fa fa-repeat"></i>'
32282 cls : 'btn-group roo-upload-cropbox-rotate-left',
32283 action : 'rotate-left',
32287 cls : 'btn btn-default',
32288 html : '<i class="fa fa-undo"></i>'
32294 cls : 'btn-group roo-upload-cropbox-download',
32295 action : 'download',
32299 cls : 'btn btn-default',
32300 html : '<i class="fa fa-download"></i>'
32306 cls : 'btn-group roo-upload-cropbox-crop',
32311 cls : 'btn btn-default',
32312 html : '<i class="fa fa-crop"></i>'
32318 cls : 'btn-group roo-upload-cropbox-trash',
32323 cls : 'btn btn-default',
32324 html : '<i class="fa fa-trash"></i>'
32330 cls : 'btn-group roo-upload-cropbox-rotate-right',
32331 action : 'rotate-right',
32335 cls : 'btn btn-default',
32336 html : '<i class="fa fa-repeat"></i>'
32344 cls : 'btn-group roo-upload-cropbox-rotate-left',
32345 action : 'rotate-left',
32349 cls : 'btn btn-default',
32350 html : '<i class="fa fa-undo"></i>'
32356 cls : 'btn-group roo-upload-cropbox-rotate-right',
32357 action : 'rotate-right',
32361 cls : 'btn btn-default',
32362 html : '<i class="fa fa-repeat"></i>'
32375 * @class Roo.bootstrap.DocumentManager
32376 * @extends Roo.bootstrap.Component
32377 * Bootstrap DocumentManager class
32378 * @cfg {String} paramName default 'imageUpload'
32379 * @cfg {String} toolTipName default 'filename'
32380 * @cfg {String} method default POST
32381 * @cfg {String} url action url
32382 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32383 * @cfg {Boolean} multiple multiple upload default true
32384 * @cfg {Number} thumbSize default 300
32385 * @cfg {String} fieldLabel
32386 * @cfg {Number} labelWidth default 4
32387 * @cfg {String} labelAlign (left|top) default left
32388 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32389 * @cfg {Number} labellg set the width of label (1-12)
32390 * @cfg {Number} labelmd set the width of label (1-12)
32391 * @cfg {Number} labelsm set the width of label (1-12)
32392 * @cfg {Number} labelxs set the width of label (1-12)
32395 * Create a new DocumentManager
32396 * @param {Object} config The config object
32399 Roo.bootstrap.DocumentManager = function(config){
32400 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32403 this.delegates = [];
32408 * Fire when initial the DocumentManager
32409 * @param {Roo.bootstrap.DocumentManager} this
32414 * inspect selected file
32415 * @param {Roo.bootstrap.DocumentManager} this
32416 * @param {File} file
32421 * Fire when xhr load exception
32422 * @param {Roo.bootstrap.DocumentManager} this
32423 * @param {XMLHttpRequest} xhr
32425 "exception" : true,
32427 * @event afterupload
32428 * Fire when xhr load exception
32429 * @param {Roo.bootstrap.DocumentManager} this
32430 * @param {XMLHttpRequest} xhr
32432 "afterupload" : true,
32435 * prepare the form data
32436 * @param {Roo.bootstrap.DocumentManager} this
32437 * @param {Object} formData
32442 * Fire when remove the file
32443 * @param {Roo.bootstrap.DocumentManager} this
32444 * @param {Object} file
32449 * Fire after refresh the file
32450 * @param {Roo.bootstrap.DocumentManager} this
32455 * Fire after click the image
32456 * @param {Roo.bootstrap.DocumentManager} this
32457 * @param {Object} file
32462 * Fire when upload a image and editable set to true
32463 * @param {Roo.bootstrap.DocumentManager} this
32464 * @param {Object} file
32468 * @event beforeselectfile
32469 * Fire before select file
32470 * @param {Roo.bootstrap.DocumentManager} this
32472 "beforeselectfile" : true,
32475 * Fire before process file
32476 * @param {Roo.bootstrap.DocumentManager} this
32477 * @param {Object} file
32481 * @event previewrendered
32482 * Fire when preview rendered
32483 * @param {Roo.bootstrap.DocumentManager} this
32484 * @param {Object} file
32486 "previewrendered" : true,
32489 "previewResize" : true
32494 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32503 paramName : 'imageUpload',
32504 toolTipName : 'filename',
32507 labelAlign : 'left',
32517 getAutoCreate : function()
32519 var managerWidget = {
32521 cls : 'roo-document-manager',
32525 cls : 'roo-document-manager-selector',
32530 cls : 'roo-document-manager-uploader',
32534 cls : 'roo-document-manager-upload-btn',
32535 html : '<i class="fa fa-plus"></i>'
32546 cls : 'column col-md-12',
32551 if(this.fieldLabel.length){
32556 cls : 'column col-md-12',
32557 html : this.fieldLabel
32561 cls : 'column col-md-12',
32566 if(this.labelAlign == 'left'){
32571 html : this.fieldLabel
32580 if(this.labelWidth > 12){
32581 content[0].style = "width: " + this.labelWidth + 'px';
32584 if(this.labelWidth < 13 && this.labelmd == 0){
32585 this.labelmd = this.labelWidth;
32588 if(this.labellg > 0){
32589 content[0].cls += ' col-lg-' + this.labellg;
32590 content[1].cls += ' col-lg-' + (12 - this.labellg);
32593 if(this.labelmd > 0){
32594 content[0].cls += ' col-md-' + this.labelmd;
32595 content[1].cls += ' col-md-' + (12 - this.labelmd);
32598 if(this.labelsm > 0){
32599 content[0].cls += ' col-sm-' + this.labelsm;
32600 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32603 if(this.labelxs > 0){
32604 content[0].cls += ' col-xs-' + this.labelxs;
32605 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32613 cls : 'row clearfix',
32621 initEvents : function()
32623 this.managerEl = this.el.select('.roo-document-manager', true).first();
32624 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32626 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32627 this.selectorEl.hide();
32630 this.selectorEl.attr('multiple', 'multiple');
32633 this.selectorEl.on('change', this.onFileSelected, this);
32635 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32636 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32638 this.uploader.on('click', this.onUploaderClick, this);
32640 this.renderProgressDialog();
32644 window.addEventListener("resize", function() { _this.refresh(); } );
32646 this.fireEvent('initial', this);
32649 renderProgressDialog : function()
32653 this.progressDialog = new Roo.bootstrap.Modal({
32654 cls : 'roo-document-manager-progress-dialog',
32655 allow_close : false,
32666 btnclick : function() {
32667 _this.uploadCancel();
32673 this.progressDialog.render(Roo.get(document.body));
32675 this.progress = new Roo.bootstrap.Progress({
32676 cls : 'roo-document-manager-progress',
32681 this.progress.render(this.progressDialog.getChildContainer());
32683 this.progressBar = new Roo.bootstrap.ProgressBar({
32684 cls : 'roo-document-manager-progress-bar',
32687 aria_valuemax : 12,
32691 this.progressBar.render(this.progress.getChildContainer());
32694 onUploaderClick : function(e)
32696 e.preventDefault();
32698 if(this.fireEvent('beforeselectfile', this) != false){
32699 this.selectorEl.dom.click();
32704 onFileSelected : function(e)
32706 e.preventDefault();
32708 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32712 Roo.each(this.selectorEl.dom.files, function(file){
32713 if(this.fireEvent('inspect', this, file) != false){
32714 this.files.push(file);
32724 this.selectorEl.dom.value = '';
32726 if(!this.files || !this.files.length){
32730 if(this.boxes > 0 && this.files.length > this.boxes){
32731 this.files = this.files.slice(0, this.boxes);
32734 this.uploader.show();
32736 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32737 this.uploader.hide();
32746 Roo.each(this.files, function(file){
32748 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32749 var f = this.renderPreview(file);
32754 if(file.type.indexOf('image') != -1){
32755 this.delegates.push(
32757 _this.process(file);
32758 }).createDelegate(this)
32766 _this.process(file);
32767 }).createDelegate(this)
32772 this.files = files;
32774 this.delegates = this.delegates.concat(docs);
32776 if(!this.delegates.length){
32781 this.progressBar.aria_valuemax = this.delegates.length;
32788 arrange : function()
32790 if(!this.delegates.length){
32791 this.progressDialog.hide();
32796 var delegate = this.delegates.shift();
32798 this.progressDialog.show();
32800 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32802 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32807 refresh : function()
32809 this.uploader.show();
32811 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32812 this.uploader.hide();
32815 Roo.isTouch ? this.closable(false) : this.closable(true);
32817 this.fireEvent('refresh', this);
32820 onRemove : function(e, el, o)
32822 e.preventDefault();
32824 this.fireEvent('remove', this, o);
32828 remove : function(o)
32832 Roo.each(this.files, function(file){
32833 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32842 this.files = files;
32849 Roo.each(this.files, function(file){
32854 file.target.remove();
32863 onClick : function(e, el, o)
32865 e.preventDefault();
32867 this.fireEvent('click', this, o);
32871 closable : function(closable)
32873 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32875 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32887 xhrOnLoad : function(xhr)
32889 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32893 if (xhr.readyState !== 4) {
32895 this.fireEvent('exception', this, xhr);
32899 var response = Roo.decode(xhr.responseText);
32901 if(!response.success){
32903 this.fireEvent('exception', this, xhr);
32907 var file = this.renderPreview(response.data);
32909 this.files.push(file);
32913 this.fireEvent('afterupload', this, xhr);
32917 xhrOnError : function(xhr)
32919 Roo.log('xhr on error');
32921 var response = Roo.decode(xhr.responseText);
32928 process : function(file)
32930 if(this.fireEvent('process', this, file) !== false){
32931 if(this.editable && file.type.indexOf('image') != -1){
32932 this.fireEvent('edit', this, file);
32936 this.uploadStart(file, false);
32943 uploadStart : function(file, crop)
32945 this.xhr = new XMLHttpRequest();
32947 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32952 file.xhr = this.xhr;
32954 this.managerEl.createChild({
32956 cls : 'roo-document-manager-loading',
32960 tooltip : file.name,
32961 cls : 'roo-document-manager-thumb',
32962 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32968 this.xhr.open(this.method, this.url, true);
32971 "Accept": "application/json",
32972 "Cache-Control": "no-cache",
32973 "X-Requested-With": "XMLHttpRequest"
32976 for (var headerName in headers) {
32977 var headerValue = headers[headerName];
32979 this.xhr.setRequestHeader(headerName, headerValue);
32985 this.xhr.onload = function()
32987 _this.xhrOnLoad(_this.xhr);
32990 this.xhr.onerror = function()
32992 _this.xhrOnError(_this.xhr);
32995 var formData = new FormData();
32997 formData.append('returnHTML', 'NO');
33000 formData.append('crop', crop);
33003 formData.append(this.paramName, file, file.name);
33010 if(this.fireEvent('prepare', this, formData, options) != false){
33012 if(options.manually){
33016 this.xhr.send(formData);
33020 this.uploadCancel();
33023 uploadCancel : function()
33029 this.delegates = [];
33031 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33038 renderPreview : function(file)
33040 if(typeof(file.target) != 'undefined' && file.target){
33044 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33046 var previewEl = this.managerEl.createChild({
33048 cls : 'roo-document-manager-preview',
33052 tooltip : file[this.toolTipName],
33053 cls : 'roo-document-manager-thumb',
33054 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33059 html : '<i class="fa fa-times-circle"></i>'
33064 var close = previewEl.select('button.close', true).first();
33066 close.on('click', this.onRemove, this, file);
33068 file.target = previewEl;
33070 var image = previewEl.select('img', true).first();
33074 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33076 image.on('click', this.onClick, this, file);
33078 this.fireEvent('previewrendered', this, file);
33084 onPreviewLoad : function(file, image)
33086 if(typeof(file.target) == 'undefined' || !file.target){
33090 var width = image.dom.naturalWidth || image.dom.width;
33091 var height = image.dom.naturalHeight || image.dom.height;
33093 if(!this.previewResize) {
33097 if(width > height){
33098 file.target.addClass('wide');
33102 file.target.addClass('tall');
33107 uploadFromSource : function(file, crop)
33109 this.xhr = new XMLHttpRequest();
33111 this.managerEl.createChild({
33113 cls : 'roo-document-manager-loading',
33117 tooltip : file.name,
33118 cls : 'roo-document-manager-thumb',
33119 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33125 this.xhr.open(this.method, this.url, true);
33128 "Accept": "application/json",
33129 "Cache-Control": "no-cache",
33130 "X-Requested-With": "XMLHttpRequest"
33133 for (var headerName in headers) {
33134 var headerValue = headers[headerName];
33136 this.xhr.setRequestHeader(headerName, headerValue);
33142 this.xhr.onload = function()
33144 _this.xhrOnLoad(_this.xhr);
33147 this.xhr.onerror = function()
33149 _this.xhrOnError(_this.xhr);
33152 var formData = new FormData();
33154 formData.append('returnHTML', 'NO');
33156 formData.append('crop', crop);
33158 if(typeof(file.filename) != 'undefined'){
33159 formData.append('filename', file.filename);
33162 if(typeof(file.mimetype) != 'undefined'){
33163 formData.append('mimetype', file.mimetype);
33168 if(this.fireEvent('prepare', this, formData) != false){
33169 this.xhr.send(formData);
33179 * @class Roo.bootstrap.DocumentViewer
33180 * @extends Roo.bootstrap.Component
33181 * Bootstrap DocumentViewer class
33182 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33183 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33186 * Create a new DocumentViewer
33187 * @param {Object} config The config object
33190 Roo.bootstrap.DocumentViewer = function(config){
33191 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33196 * Fire after initEvent
33197 * @param {Roo.bootstrap.DocumentViewer} this
33203 * @param {Roo.bootstrap.DocumentViewer} this
33208 * Fire after download button
33209 * @param {Roo.bootstrap.DocumentViewer} this
33214 * Fire after trash button
33215 * @param {Roo.bootstrap.DocumentViewer} this
33222 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33224 showDownload : true,
33228 getAutoCreate : function()
33232 cls : 'roo-document-viewer',
33236 cls : 'roo-document-viewer-body',
33240 cls : 'roo-document-viewer-thumb',
33244 cls : 'roo-document-viewer-image'
33252 cls : 'roo-document-viewer-footer',
33255 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33259 cls : 'btn-group roo-document-viewer-download',
33263 cls : 'btn btn-default',
33264 html : '<i class="fa fa-download"></i>'
33270 cls : 'btn-group roo-document-viewer-trash',
33274 cls : 'btn btn-default',
33275 html : '<i class="fa fa-trash"></i>'
33288 initEvents : function()
33290 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33291 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33293 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33294 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33296 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33297 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33299 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33300 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33302 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33303 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33305 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33306 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33308 this.bodyEl.on('click', this.onClick, this);
33309 this.downloadBtn.on('click', this.onDownload, this);
33310 this.trashBtn.on('click', this.onTrash, this);
33312 this.downloadBtn.hide();
33313 this.trashBtn.hide();
33315 if(this.showDownload){
33316 this.downloadBtn.show();
33319 if(this.showTrash){
33320 this.trashBtn.show();
33323 if(!this.showDownload && !this.showTrash) {
33324 this.footerEl.hide();
33329 initial : function()
33331 this.fireEvent('initial', this);
33335 onClick : function(e)
33337 e.preventDefault();
33339 this.fireEvent('click', this);
33342 onDownload : function(e)
33344 e.preventDefault();
33346 this.fireEvent('download', this);
33349 onTrash : function(e)
33351 e.preventDefault();
33353 this.fireEvent('trash', this);
33365 * @class Roo.bootstrap.FieldLabel
33366 * @extends Roo.bootstrap.Component
33367 * Bootstrap FieldLabel class
33368 * @cfg {String} html contents of the element
33369 * @cfg {String} tag tag of the element default label
33370 * @cfg {String} cls class of the element
33371 * @cfg {String} target label target
33372 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33373 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33374 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33375 * @cfg {String} iconTooltip default "This field is required"
33376 * @cfg {String} indicatorpos (left|right) default left
33379 * Create a new FieldLabel
33380 * @param {Object} config The config object
33383 Roo.bootstrap.FieldLabel = function(config){
33384 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33389 * Fires after the field has been marked as invalid.
33390 * @param {Roo.form.FieldLabel} this
33391 * @param {String} msg The validation message
33396 * Fires after the field has been validated with no errors.
33397 * @param {Roo.form.FieldLabel} this
33403 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33410 invalidClass : 'has-warning',
33411 validClass : 'has-success',
33412 iconTooltip : 'This field is required',
33413 indicatorpos : 'left',
33415 getAutoCreate : function(){
33418 if (!this.allowBlank) {
33424 cls : 'roo-bootstrap-field-label ' + this.cls,
33429 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33430 tooltip : this.iconTooltip
33439 if(this.indicatorpos == 'right'){
33442 cls : 'roo-bootstrap-field-label ' + this.cls,
33451 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33452 tooltip : this.iconTooltip
33461 initEvents: function()
33463 Roo.bootstrap.Element.superclass.initEvents.call(this);
33465 this.indicator = this.indicatorEl();
33467 if(this.indicator){
33468 this.indicator.removeClass('visible');
33469 this.indicator.addClass('invisible');
33472 Roo.bootstrap.FieldLabel.register(this);
33475 indicatorEl : function()
33477 var indicator = this.el.select('i.roo-required-indicator',true).first();
33488 * Mark this field as valid
33490 markValid : function()
33492 if(this.indicator){
33493 this.indicator.removeClass('visible');
33494 this.indicator.addClass('invisible');
33496 if (Roo.bootstrap.version == 3) {
33497 this.el.removeClass(this.invalidClass);
33498 this.el.addClass(this.validClass);
33500 this.el.removeClass('is-invalid');
33501 this.el.addClass('is-valid');
33505 this.fireEvent('valid', this);
33509 * Mark this field as invalid
33510 * @param {String} msg The validation message
33512 markInvalid : function(msg)
33514 if(this.indicator){
33515 this.indicator.removeClass('invisible');
33516 this.indicator.addClass('visible');
33518 if (Roo.bootstrap.version == 3) {
33519 this.el.removeClass(this.validClass);
33520 this.el.addClass(this.invalidClass);
33522 this.el.removeClass('is-valid');
33523 this.el.addClass('is-invalid');
33527 this.fireEvent('invalid', this, msg);
33533 Roo.apply(Roo.bootstrap.FieldLabel, {
33538 * register a FieldLabel Group
33539 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33541 register : function(label)
33543 if(this.groups.hasOwnProperty(label.target)){
33547 this.groups[label.target] = label;
33551 * fetch a FieldLabel Group based on the target
33552 * @param {string} target
33553 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33555 get: function(target) {
33556 if (typeof(this.groups[target]) == 'undefined') {
33560 return this.groups[target] ;
33569 * page DateSplitField.
33575 * @class Roo.bootstrap.DateSplitField
33576 * @extends Roo.bootstrap.Component
33577 * Bootstrap DateSplitField class
33578 * @cfg {string} fieldLabel - the label associated
33579 * @cfg {Number} labelWidth set the width of label (0-12)
33580 * @cfg {String} labelAlign (top|left)
33581 * @cfg {Boolean} dayAllowBlank (true|false) default false
33582 * @cfg {Boolean} monthAllowBlank (true|false) default false
33583 * @cfg {Boolean} yearAllowBlank (true|false) default false
33584 * @cfg {string} dayPlaceholder
33585 * @cfg {string} monthPlaceholder
33586 * @cfg {string} yearPlaceholder
33587 * @cfg {string} dayFormat default 'd'
33588 * @cfg {string} monthFormat default 'm'
33589 * @cfg {string} yearFormat default 'Y'
33590 * @cfg {Number} labellg set the width of label (1-12)
33591 * @cfg {Number} labelmd set the width of label (1-12)
33592 * @cfg {Number} labelsm set the width of label (1-12)
33593 * @cfg {Number} labelxs set the width of label (1-12)
33597 * Create a new DateSplitField
33598 * @param {Object} config The config object
33601 Roo.bootstrap.DateSplitField = function(config){
33602 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33608 * getting the data of years
33609 * @param {Roo.bootstrap.DateSplitField} this
33610 * @param {Object} years
33615 * getting the data of days
33616 * @param {Roo.bootstrap.DateSplitField} this
33617 * @param {Object} days
33622 * Fires after the field has been marked as invalid.
33623 * @param {Roo.form.Field} this
33624 * @param {String} msg The validation message
33629 * Fires after the field has been validated with no errors.
33630 * @param {Roo.form.Field} this
33636 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33639 labelAlign : 'top',
33641 dayAllowBlank : false,
33642 monthAllowBlank : false,
33643 yearAllowBlank : false,
33644 dayPlaceholder : '',
33645 monthPlaceholder : '',
33646 yearPlaceholder : '',
33650 isFormField : true,
33656 getAutoCreate : function()
33660 cls : 'row roo-date-split-field-group',
33665 cls : 'form-hidden-field roo-date-split-field-group-value',
33671 var labelCls = 'col-md-12';
33672 var contentCls = 'col-md-4';
33674 if(this.fieldLabel){
33678 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33682 html : this.fieldLabel
33687 if(this.labelAlign == 'left'){
33689 if(this.labelWidth > 12){
33690 label.style = "width: " + this.labelWidth + 'px';
33693 if(this.labelWidth < 13 && this.labelmd == 0){
33694 this.labelmd = this.labelWidth;
33697 if(this.labellg > 0){
33698 labelCls = ' col-lg-' + this.labellg;
33699 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33702 if(this.labelmd > 0){
33703 labelCls = ' col-md-' + this.labelmd;
33704 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33707 if(this.labelsm > 0){
33708 labelCls = ' col-sm-' + this.labelsm;
33709 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33712 if(this.labelxs > 0){
33713 labelCls = ' col-xs-' + this.labelxs;
33714 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33718 label.cls += ' ' + labelCls;
33720 cfg.cn.push(label);
33723 Roo.each(['day', 'month', 'year'], function(t){
33726 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33733 inputEl: function ()
33735 return this.el.select('.roo-date-split-field-group-value', true).first();
33738 onRender : function(ct, position)
33742 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33744 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33746 this.dayField = new Roo.bootstrap.ComboBox({
33747 allowBlank : this.dayAllowBlank,
33748 alwaysQuery : true,
33749 displayField : 'value',
33752 forceSelection : true,
33754 placeholder : this.dayPlaceholder,
33755 selectOnFocus : true,
33756 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33757 triggerAction : 'all',
33759 valueField : 'value',
33760 store : new Roo.data.SimpleStore({
33761 data : (function() {
33763 _this.fireEvent('days', _this, days);
33766 fields : [ 'value' ]
33769 select : function (_self, record, index)
33771 _this.setValue(_this.getValue());
33776 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33778 this.monthField = new Roo.bootstrap.MonthField({
33779 after : '<i class=\"fa fa-calendar\"></i>',
33780 allowBlank : this.monthAllowBlank,
33781 placeholder : this.monthPlaceholder,
33784 render : function (_self)
33786 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33787 e.preventDefault();
33791 select : function (_self, oldvalue, newvalue)
33793 _this.setValue(_this.getValue());
33798 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33800 this.yearField = new Roo.bootstrap.ComboBox({
33801 allowBlank : this.yearAllowBlank,
33802 alwaysQuery : true,
33803 displayField : 'value',
33806 forceSelection : true,
33808 placeholder : this.yearPlaceholder,
33809 selectOnFocus : true,
33810 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33811 triggerAction : 'all',
33813 valueField : 'value',
33814 store : new Roo.data.SimpleStore({
33815 data : (function() {
33817 _this.fireEvent('years', _this, years);
33820 fields : [ 'value' ]
33823 select : function (_self, record, index)
33825 _this.setValue(_this.getValue());
33830 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33833 setValue : function(v, format)
33835 this.inputEl.dom.value = v;
33837 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33839 var d = Date.parseDate(v, f);
33846 this.setDay(d.format(this.dayFormat));
33847 this.setMonth(d.format(this.monthFormat));
33848 this.setYear(d.format(this.yearFormat));
33855 setDay : function(v)
33857 this.dayField.setValue(v);
33858 this.inputEl.dom.value = this.getValue();
33863 setMonth : function(v)
33865 this.monthField.setValue(v, true);
33866 this.inputEl.dom.value = this.getValue();
33871 setYear : function(v)
33873 this.yearField.setValue(v);
33874 this.inputEl.dom.value = this.getValue();
33879 getDay : function()
33881 return this.dayField.getValue();
33884 getMonth : function()
33886 return this.monthField.getValue();
33889 getYear : function()
33891 return this.yearField.getValue();
33894 getValue : function()
33896 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33898 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33908 this.inputEl.dom.value = '';
33913 validate : function()
33915 var d = this.dayField.validate();
33916 var m = this.monthField.validate();
33917 var y = this.yearField.validate();
33922 (!this.dayAllowBlank && !d) ||
33923 (!this.monthAllowBlank && !m) ||
33924 (!this.yearAllowBlank && !y)
33929 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33938 this.markInvalid();
33943 markValid : function()
33946 var label = this.el.select('label', true).first();
33947 var icon = this.el.select('i.fa-star', true).first();
33953 this.fireEvent('valid', this);
33957 * Mark this field as invalid
33958 * @param {String} msg The validation message
33960 markInvalid : function(msg)
33963 var label = this.el.select('label', true).first();
33964 var icon = this.el.select('i.fa-star', true).first();
33966 if(label && !icon){
33967 this.el.select('.roo-date-split-field-label', true).createChild({
33969 cls : 'text-danger fa fa-lg fa-star',
33970 tooltip : 'This field is required',
33971 style : 'margin-right:5px;'
33975 this.fireEvent('invalid', this, msg);
33978 clearInvalid : function()
33980 var label = this.el.select('label', true).first();
33981 var icon = this.el.select('i.fa-star', true).first();
33987 this.fireEvent('valid', this);
33990 getName: function()
34000 * @class Roo.bootstrap.LayoutMasonry
34001 * @extends Roo.bootstrap.Component
34002 * @children Roo.bootstrap.Element Roo.bootstrap.Image Roo.bootstrap.MasonryBrick
34003 * Bootstrap Layout Masonry class
34006 * http://masonry.desandro.com
34008 * The idea is to render all the bricks based on vertical width...
34010 * The original code extends 'outlayer' - we might need to use that....
34013 * Create a new Element
34014 * @param {Object} config The config object
34017 Roo.bootstrap.LayoutMasonry = function(config){
34019 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34023 Roo.bootstrap.LayoutMasonry.register(this);
34029 * Fire after layout the items
34030 * @param {Roo.bootstrap.LayoutMasonry} this
34031 * @param {Roo.EventObject} e
34038 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34041 * @cfg {Boolean} isLayoutInstant = no animation?
34043 isLayoutInstant : false, // needed?
34046 * @cfg {Number} boxWidth width of the columns
34051 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34056 * @cfg {Number} padWidth padding below box..
34061 * @cfg {Number} gutter gutter width..
34066 * @cfg {Number} maxCols maximum number of columns
34072 * @cfg {Boolean} isAutoInitial defalut true
34074 isAutoInitial : true,
34079 * @cfg {Boolean} isHorizontal defalut false
34081 isHorizontal : false,
34083 currentSize : null,
34089 bricks: null, //CompositeElement
34093 _isLayoutInited : false,
34095 // isAlternative : false, // only use for vertical layout...
34098 * @cfg {Number} alternativePadWidth padding below box..
34100 alternativePadWidth : 50,
34102 selectedBrick : [],
34104 getAutoCreate : function(){
34106 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34110 cls: 'blog-masonary-wrapper ' + this.cls,
34112 cls : 'mas-boxes masonary'
34119 getChildContainer: function( )
34121 if (this.boxesEl) {
34122 return this.boxesEl;
34125 this.boxesEl = this.el.select('.mas-boxes').first();
34127 return this.boxesEl;
34131 initEvents : function()
34135 if(this.isAutoInitial){
34136 Roo.log('hook children rendered');
34137 this.on('childrenrendered', function() {
34138 Roo.log('children rendered');
34144 initial : function()
34146 this.selectedBrick = [];
34148 this.currentSize = this.el.getBox(true);
34150 Roo.EventManager.onWindowResize(this.resize, this);
34152 if(!this.isAutoInitial){
34160 //this.layout.defer(500,this);
34164 resize : function()
34166 var cs = this.el.getBox(true);
34169 this.currentSize.width == cs.width &&
34170 this.currentSize.x == cs.x &&
34171 this.currentSize.height == cs.height &&
34172 this.currentSize.y == cs.y
34174 Roo.log("no change in with or X or Y");
34178 this.currentSize = cs;
34184 layout : function()
34186 this._resetLayout();
34188 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34190 this.layoutItems( isInstant );
34192 this._isLayoutInited = true;
34194 this.fireEvent('layout', this);
34198 _resetLayout : function()
34200 if(this.isHorizontal){
34201 this.horizontalMeasureColumns();
34205 this.verticalMeasureColumns();
34209 verticalMeasureColumns : function()
34211 this.getContainerWidth();
34213 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34214 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34218 var boxWidth = this.boxWidth + this.padWidth;
34220 if(this.containerWidth < this.boxWidth){
34221 boxWidth = this.containerWidth
34224 var containerWidth = this.containerWidth;
34226 var cols = Math.floor(containerWidth / boxWidth);
34228 this.cols = Math.max( cols, 1 );
34230 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34232 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34234 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34236 this.colWidth = boxWidth + avail - this.padWidth;
34238 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34239 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34242 horizontalMeasureColumns : function()
34244 this.getContainerWidth();
34246 var boxWidth = this.boxWidth;
34248 if(this.containerWidth < boxWidth){
34249 boxWidth = this.containerWidth;
34252 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34254 this.el.setHeight(boxWidth);
34258 getContainerWidth : function()
34260 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34263 layoutItems : function( isInstant )
34265 Roo.log(this.bricks);
34267 var items = Roo.apply([], this.bricks);
34269 if(this.isHorizontal){
34270 this._horizontalLayoutItems( items , isInstant );
34274 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34275 // this._verticalAlternativeLayoutItems( items , isInstant );
34279 this._verticalLayoutItems( items , isInstant );
34283 _verticalLayoutItems : function ( items , isInstant)
34285 if ( !items || !items.length ) {
34290 ['xs', 'xs', 'xs', 'tall'],
34291 ['xs', 'xs', 'tall'],
34292 ['xs', 'xs', 'sm'],
34293 ['xs', 'xs', 'xs'],
34299 ['sm', 'xs', 'xs'],
34303 ['tall', 'xs', 'xs', 'xs'],
34304 ['tall', 'xs', 'xs'],
34316 Roo.each(items, function(item, k){
34318 switch (item.size) {
34319 // these layouts take up a full box,
34330 boxes.push([item]);
34353 var filterPattern = function(box, length)
34361 var pattern = box.slice(0, length);
34365 Roo.each(pattern, function(i){
34366 format.push(i.size);
34369 Roo.each(standard, function(s){
34371 if(String(s) != String(format)){
34380 if(!match && length == 1){
34385 filterPattern(box, length - 1);
34389 queue.push(pattern);
34391 box = box.slice(length, box.length);
34393 filterPattern(box, 4);
34399 Roo.each(boxes, function(box, k){
34405 if(box.length == 1){
34410 filterPattern(box, 4);
34414 this._processVerticalLayoutQueue( queue, isInstant );
34418 // _verticalAlternativeLayoutItems : function( items , isInstant )
34420 // if ( !items || !items.length ) {
34424 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34428 _horizontalLayoutItems : function ( items , isInstant)
34430 if ( !items || !items.length || items.length < 3) {
34436 var eItems = items.slice(0, 3);
34438 items = items.slice(3, items.length);
34441 ['xs', 'xs', 'xs', 'wide'],
34442 ['xs', 'xs', 'wide'],
34443 ['xs', 'xs', 'sm'],
34444 ['xs', 'xs', 'xs'],
34450 ['sm', 'xs', 'xs'],
34454 ['wide', 'xs', 'xs', 'xs'],
34455 ['wide', 'xs', 'xs'],
34468 Roo.each(items, function(item, k){
34470 switch (item.size) {
34481 boxes.push([item]);
34505 var filterPattern = function(box, length)
34513 var pattern = box.slice(0, length);
34517 Roo.each(pattern, function(i){
34518 format.push(i.size);
34521 Roo.each(standard, function(s){
34523 if(String(s) != String(format)){
34532 if(!match && length == 1){
34537 filterPattern(box, length - 1);
34541 queue.push(pattern);
34543 box = box.slice(length, box.length);
34545 filterPattern(box, 4);
34551 Roo.each(boxes, function(box, k){
34557 if(box.length == 1){
34562 filterPattern(box, 4);
34569 var pos = this.el.getBox(true);
34573 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34575 var hit_end = false;
34577 Roo.each(queue, function(box){
34581 Roo.each(box, function(b){
34583 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34593 Roo.each(box, function(b){
34595 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34598 mx = Math.max(mx, b.x);
34602 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34606 Roo.each(box, function(b){
34608 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34622 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34625 /** Sets position of item in DOM
34626 * @param {Element} item
34627 * @param {Number} x - horizontal position
34628 * @param {Number} y - vertical position
34629 * @param {Boolean} isInstant - disables transitions
34631 _processVerticalLayoutQueue : function( queue, isInstant )
34633 var pos = this.el.getBox(true);
34638 for (var i = 0; i < this.cols; i++){
34642 Roo.each(queue, function(box, k){
34644 var col = k % this.cols;
34646 Roo.each(box, function(b,kk){
34648 b.el.position('absolute');
34650 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34651 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34653 if(b.size == 'md-left' || b.size == 'md-right'){
34654 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34655 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34658 b.el.setWidth(width);
34659 b.el.setHeight(height);
34661 b.el.select('iframe',true).setSize(width,height);
34665 for (var i = 0; i < this.cols; i++){
34667 if(maxY[i] < maxY[col]){
34672 col = Math.min(col, i);
34676 x = pos.x + col * (this.colWidth + this.padWidth);
34680 var positions = [];
34682 switch (box.length){
34684 positions = this.getVerticalOneBoxColPositions(x, y, box);
34687 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34690 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34693 positions = this.getVerticalFourBoxColPositions(x, y, box);
34699 Roo.each(box, function(b,kk){
34701 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34703 var sz = b.el.getSize();
34705 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34713 for (var i = 0; i < this.cols; i++){
34714 mY = Math.max(mY, maxY[i]);
34717 this.el.setHeight(mY - pos.y);
34721 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34723 // var pos = this.el.getBox(true);
34726 // var maxX = pos.right;
34728 // var maxHeight = 0;
34730 // Roo.each(items, function(item, k){
34734 // item.el.position('absolute');
34736 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34738 // item.el.setWidth(width);
34740 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34742 // item.el.setHeight(height);
34745 // item.el.setXY([x, y], isInstant ? false : true);
34747 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34750 // y = y + height + this.alternativePadWidth;
34752 // maxHeight = maxHeight + height + this.alternativePadWidth;
34756 // this.el.setHeight(maxHeight);
34760 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34762 var pos = this.el.getBox(true);
34767 var maxX = pos.right;
34769 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34771 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34773 Roo.each(queue, function(box, k){
34775 Roo.each(box, function(b, kk){
34777 b.el.position('absolute');
34779 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34780 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34782 if(b.size == 'md-left' || b.size == 'md-right'){
34783 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34784 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34787 b.el.setWidth(width);
34788 b.el.setHeight(height);
34796 var positions = [];
34798 switch (box.length){
34800 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34803 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34806 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34809 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34815 Roo.each(box, function(b,kk){
34817 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34819 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34827 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34829 Roo.each(eItems, function(b,k){
34831 b.size = (k == 0) ? 'sm' : 'xs';
34832 b.x = (k == 0) ? 2 : 1;
34833 b.y = (k == 0) ? 2 : 1;
34835 b.el.position('absolute');
34837 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34839 b.el.setWidth(width);
34841 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34843 b.el.setHeight(height);
34847 var positions = [];
34850 x : maxX - this.unitWidth * 2 - this.gutter,
34855 x : maxX - this.unitWidth,
34856 y : minY + (this.unitWidth + this.gutter) * 2
34860 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34864 Roo.each(eItems, function(b,k){
34866 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34872 getVerticalOneBoxColPositions : function(x, y, box)
34876 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34878 if(box[0].size == 'md-left'){
34882 if(box[0].size == 'md-right'){
34887 x : x + (this.unitWidth + this.gutter) * rand,
34894 getVerticalTwoBoxColPositions : function(x, y, box)
34898 if(box[0].size == 'xs'){
34902 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34906 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34920 x : x + (this.unitWidth + this.gutter) * 2,
34921 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34928 getVerticalThreeBoxColPositions : function(x, y, box)
34932 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34940 x : x + (this.unitWidth + this.gutter) * 1,
34945 x : x + (this.unitWidth + this.gutter) * 2,
34953 if(box[0].size == 'xs' && box[1].size == 'xs'){
34962 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34966 x : x + (this.unitWidth + this.gutter) * 1,
34980 x : x + (this.unitWidth + this.gutter) * 2,
34985 x : x + (this.unitWidth + this.gutter) * 2,
34986 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34993 getVerticalFourBoxColPositions : function(x, y, box)
34997 if(box[0].size == 'xs'){
35006 y : y + (this.unitHeight + this.gutter) * 1
35011 y : y + (this.unitHeight + this.gutter) * 2
35015 x : x + (this.unitWidth + this.gutter) * 1,
35029 x : x + (this.unitWidth + this.gutter) * 2,
35034 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35035 y : y + (this.unitHeight + this.gutter) * 1
35039 x : x + (this.unitWidth + this.gutter) * 2,
35040 y : y + (this.unitWidth + this.gutter) * 2
35047 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35051 if(box[0].size == 'md-left'){
35053 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35060 if(box[0].size == 'md-right'){
35062 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35063 y : minY + (this.unitWidth + this.gutter) * 1
35069 var rand = Math.floor(Math.random() * (4 - box[0].y));
35072 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35073 y : minY + (this.unitWidth + this.gutter) * rand
35080 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35084 if(box[0].size == 'xs'){
35087 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35092 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35093 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35101 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35106 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35107 y : minY + (this.unitWidth + this.gutter) * 2
35114 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35118 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35121 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35126 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35127 y : minY + (this.unitWidth + this.gutter) * 1
35131 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35132 y : minY + (this.unitWidth + this.gutter) * 2
35139 if(box[0].size == 'xs' && box[1].size == 'xs'){
35142 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35147 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35152 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35153 y : minY + (this.unitWidth + this.gutter) * 1
35161 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35166 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35167 y : minY + (this.unitWidth + this.gutter) * 2
35171 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35172 y : minY + (this.unitWidth + this.gutter) * 2
35179 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35183 if(box[0].size == 'xs'){
35186 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35191 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35196 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),
35201 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35202 y : minY + (this.unitWidth + this.gutter) * 1
35210 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35215 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35216 y : minY + (this.unitWidth + this.gutter) * 2
35220 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35221 y : minY + (this.unitWidth + this.gutter) * 2
35225 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),
35226 y : minY + (this.unitWidth + this.gutter) * 2
35234 * remove a Masonry Brick
35235 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35237 removeBrick : function(brick_id)
35243 for (var i = 0; i<this.bricks.length; i++) {
35244 if (this.bricks[i].id == brick_id) {
35245 this.bricks.splice(i,1);
35246 this.el.dom.removeChild(Roo.get(brick_id).dom);
35253 * adds a Masonry Brick
35254 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35256 addBrick : function(cfg)
35258 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35259 //this.register(cn);
35260 cn.parentId = this.id;
35261 cn.render(this.el);
35266 * register a Masonry Brick
35267 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35270 register : function(brick)
35272 this.bricks.push(brick);
35273 brick.masonryId = this.id;
35277 * clear all the Masonry Brick
35279 clearAll : function()
35282 //this.getChildContainer().dom.innerHTML = "";
35283 this.el.dom.innerHTML = '';
35286 getSelected : function()
35288 if (!this.selectedBrick) {
35292 return this.selectedBrick;
35296 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35300 * register a Masonry Layout
35301 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35304 register : function(layout)
35306 this.groups[layout.id] = layout;
35309 * fetch a Masonry Layout based on the masonry layout ID
35310 * @param {string} the masonry layout to add
35311 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35314 get: function(layout_id) {
35315 if (typeof(this.groups[layout_id]) == 'undefined') {
35318 return this.groups[layout_id] ;
35330 * http://masonry.desandro.com
35332 * The idea is to render all the bricks based on vertical width...
35334 * The original code extends 'outlayer' - we might need to use that....
35340 * @class Roo.bootstrap.LayoutMasonryAuto
35341 * @extends Roo.bootstrap.Component
35342 * Bootstrap Layout Masonry class
35345 * Create a new Element
35346 * @param {Object} config The config object
35349 Roo.bootstrap.LayoutMasonryAuto = function(config){
35350 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35353 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35356 * @cfg {Boolean} isFitWidth - resize the width..
35358 isFitWidth : false, // options..
35360 * @cfg {Boolean} isOriginLeft = left align?
35362 isOriginLeft : true,
35364 * @cfg {Boolean} isOriginTop = top align?
35366 isOriginTop : false,
35368 * @cfg {Boolean} isLayoutInstant = no animation?
35370 isLayoutInstant : false, // needed?
35372 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35374 isResizingContainer : true,
35376 * @cfg {Number} columnWidth width of the columns
35382 * @cfg {Number} maxCols maximum number of columns
35387 * @cfg {Number} padHeight padding below box..
35393 * @cfg {Boolean} isAutoInitial defalut true
35396 isAutoInitial : true,
35402 initialColumnWidth : 0,
35403 currentSize : null,
35405 colYs : null, // array.
35412 bricks: null, //CompositeElement
35413 cols : 0, // array?
35414 // element : null, // wrapped now this.el
35415 _isLayoutInited : null,
35418 getAutoCreate : function(){
35422 cls: 'blog-masonary-wrapper ' + this.cls,
35424 cls : 'mas-boxes masonary'
35431 getChildContainer: function( )
35433 if (this.boxesEl) {
35434 return this.boxesEl;
35437 this.boxesEl = this.el.select('.mas-boxes').first();
35439 return this.boxesEl;
35443 initEvents : function()
35447 if(this.isAutoInitial){
35448 Roo.log('hook children rendered');
35449 this.on('childrenrendered', function() {
35450 Roo.log('children rendered');
35457 initial : function()
35459 this.reloadItems();
35461 this.currentSize = this.el.getBox(true);
35463 /// was window resize... - let's see if this works..
35464 Roo.EventManager.onWindowResize(this.resize, this);
35466 if(!this.isAutoInitial){
35471 this.layout.defer(500,this);
35474 reloadItems: function()
35476 this.bricks = this.el.select('.masonry-brick', true);
35478 this.bricks.each(function(b) {
35479 //Roo.log(b.getSize());
35480 if (!b.attr('originalwidth')) {
35481 b.attr('originalwidth', b.getSize().width);
35486 Roo.log(this.bricks.elements.length);
35489 resize : function()
35492 var cs = this.el.getBox(true);
35494 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35495 Roo.log("no change in with or X");
35498 this.currentSize = cs;
35502 layout : function()
35505 this._resetLayout();
35506 //this._manageStamps();
35508 // don't animate first layout
35509 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35510 this.layoutItems( isInstant );
35512 // flag for initalized
35513 this._isLayoutInited = true;
35516 layoutItems : function( isInstant )
35518 //var items = this._getItemsForLayout( this.items );
35519 // original code supports filtering layout items.. we just ignore it..
35521 this._layoutItems( this.bricks , isInstant );
35523 this._postLayout();
35525 _layoutItems : function ( items , isInstant)
35527 //this.fireEvent( 'layout', this, items );
35530 if ( !items || !items.elements.length ) {
35531 // no items, emit event with empty array
35536 items.each(function(item) {
35537 Roo.log("layout item");
35539 // get x/y object from method
35540 var position = this._getItemLayoutPosition( item );
35542 position.item = item;
35543 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35544 queue.push( position );
35547 this._processLayoutQueue( queue );
35549 /** Sets position of item in DOM
35550 * @param {Element} item
35551 * @param {Number} x - horizontal position
35552 * @param {Number} y - vertical position
35553 * @param {Boolean} isInstant - disables transitions
35555 _processLayoutQueue : function( queue )
35557 for ( var i=0, len = queue.length; i < len; i++ ) {
35558 var obj = queue[i];
35559 obj.item.position('absolute');
35560 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35566 * Any logic you want to do after each layout,
35567 * i.e. size the container
35569 _postLayout : function()
35571 this.resizeContainer();
35574 resizeContainer : function()
35576 if ( !this.isResizingContainer ) {
35579 var size = this._getContainerSize();
35581 this.el.setSize(size.width,size.height);
35582 this.boxesEl.setSize(size.width,size.height);
35588 _resetLayout : function()
35590 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35591 this.colWidth = this.el.getWidth();
35592 //this.gutter = this.el.getWidth();
35594 this.measureColumns();
35600 this.colYs.push( 0 );
35606 measureColumns : function()
35608 this.getContainerWidth();
35609 // if columnWidth is 0, default to outerWidth of first item
35610 if ( !this.columnWidth ) {
35611 var firstItem = this.bricks.first();
35612 Roo.log(firstItem);
35613 this.columnWidth = this.containerWidth;
35614 if (firstItem && firstItem.attr('originalwidth') ) {
35615 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35617 // columnWidth fall back to item of first element
35618 Roo.log("set column width?");
35619 this.initialColumnWidth = this.columnWidth ;
35621 // if first elem has no width, default to size of container
35626 if (this.initialColumnWidth) {
35627 this.columnWidth = this.initialColumnWidth;
35632 // column width is fixed at the top - however if container width get's smaller we should
35635 // this bit calcs how man columns..
35637 var columnWidth = this.columnWidth += this.gutter;
35639 // calculate columns
35640 var containerWidth = this.containerWidth + this.gutter;
35642 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35643 // fix rounding errors, typically with gutters
35644 var excess = columnWidth - containerWidth % columnWidth;
35647 // if overshoot is less than a pixel, round up, otherwise floor it
35648 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35649 cols = Math[ mathMethod ]( cols );
35650 this.cols = Math.max( cols, 1 );
35651 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35653 // padding positioning..
35654 var totalColWidth = this.cols * this.columnWidth;
35655 var padavail = this.containerWidth - totalColWidth;
35656 // so for 2 columns - we need 3 'pads'
35658 var padNeeded = (1+this.cols) * this.padWidth;
35660 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35662 this.columnWidth += padExtra
35663 //this.padWidth = Math.floor(padavail / ( this.cols));
35665 // adjust colum width so that padding is fixed??
35667 // we have 3 columns ... total = width * 3
35668 // we have X left over... that should be used by
35670 //if (this.expandC) {
35678 getContainerWidth : function()
35680 /* // container is parent if fit width
35681 var container = this.isFitWidth ? this.element.parentNode : this.element;
35682 // check that this.size and size are there
35683 // IE8 triggers resize on body size change, so they might not be
35685 var size = getSize( container ); //FIXME
35686 this.containerWidth = size && size.innerWidth; //FIXME
35689 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35693 _getItemLayoutPosition : function( item ) // what is item?
35695 // we resize the item to our columnWidth..
35697 item.setWidth(this.columnWidth);
35698 item.autoBoxAdjust = false;
35700 var sz = item.getSize();
35702 // how many columns does this brick span
35703 var remainder = this.containerWidth % this.columnWidth;
35705 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35706 // round if off by 1 pixel, otherwise use ceil
35707 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35708 colSpan = Math.min( colSpan, this.cols );
35710 // normally this should be '1' as we dont' currently allow multi width columns..
35712 var colGroup = this._getColGroup( colSpan );
35713 // get the minimum Y value from the columns
35714 var minimumY = Math.min.apply( Math, colGroup );
35715 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35717 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35719 // position the brick
35721 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35722 y: this.currentSize.y + minimumY + this.padHeight
35726 // apply setHeight to necessary columns
35727 var setHeight = minimumY + sz.height + this.padHeight;
35728 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35730 var setSpan = this.cols + 1 - colGroup.length;
35731 for ( var i = 0; i < setSpan; i++ ) {
35732 this.colYs[ shortColIndex + i ] = setHeight ;
35739 * @param {Number} colSpan - number of columns the element spans
35740 * @returns {Array} colGroup
35742 _getColGroup : function( colSpan )
35744 if ( colSpan < 2 ) {
35745 // if brick spans only one column, use all the column Ys
35750 // how many different places could this brick fit horizontally
35751 var groupCount = this.cols + 1 - colSpan;
35752 // for each group potential horizontal position
35753 for ( var i = 0; i < groupCount; i++ ) {
35754 // make an array of colY values for that one group
35755 var groupColYs = this.colYs.slice( i, i + colSpan );
35756 // and get the max value of the array
35757 colGroup[i] = Math.max.apply( Math, groupColYs );
35762 _manageStamp : function( stamp )
35764 var stampSize = stamp.getSize();
35765 var offset = stamp.getBox();
35766 // get the columns that this stamp affects
35767 var firstX = this.isOriginLeft ? offset.x : offset.right;
35768 var lastX = firstX + stampSize.width;
35769 var firstCol = Math.floor( firstX / this.columnWidth );
35770 firstCol = Math.max( 0, firstCol );
35772 var lastCol = Math.floor( lastX / this.columnWidth );
35773 // lastCol should not go over if multiple of columnWidth #425
35774 lastCol -= lastX % this.columnWidth ? 0 : 1;
35775 lastCol = Math.min( this.cols - 1, lastCol );
35777 // set colYs to bottom of the stamp
35778 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35781 for ( var i = firstCol; i <= lastCol; i++ ) {
35782 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35787 _getContainerSize : function()
35789 this.maxY = Math.max.apply( Math, this.colYs );
35794 if ( this.isFitWidth ) {
35795 size.width = this._getContainerFitWidth();
35801 _getContainerFitWidth : function()
35803 var unusedCols = 0;
35804 // count unused columns
35807 if ( this.colYs[i] !== 0 ) {
35812 // fit container to columns that have been used
35813 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35816 needsResizeLayout : function()
35818 var previousWidth = this.containerWidth;
35819 this.getContainerWidth();
35820 return previousWidth !== this.containerWidth;
35835 * @class Roo.bootstrap.MasonryBrick
35836 * @extends Roo.bootstrap.Component
35837 * Bootstrap MasonryBrick class
35840 * Create a new MasonryBrick
35841 * @param {Object} config The config object
35844 Roo.bootstrap.MasonryBrick = function(config){
35846 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35848 Roo.bootstrap.MasonryBrick.register(this);
35854 * When a MasonryBrick is clcik
35855 * @param {Roo.bootstrap.MasonryBrick} this
35856 * @param {Roo.EventObject} e
35862 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35865 * @cfg {String} title
35869 * @cfg {String} html
35873 * @cfg {String} bgimage
35877 * @cfg {String} videourl
35881 * @cfg {String} cls
35885 * @cfg {String} href
35889 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35894 * @cfg {String} placetitle (center|bottom)
35899 * @cfg {Boolean} isFitContainer defalut true
35901 isFitContainer : true,
35904 * @cfg {Boolean} preventDefault defalut false
35906 preventDefault : false,
35909 * @cfg {Boolean} inverse defalut false
35911 maskInverse : false,
35913 getAutoCreate : function()
35915 if(!this.isFitContainer){
35916 return this.getSplitAutoCreate();
35919 var cls = 'masonry-brick masonry-brick-full';
35921 if(this.href.length){
35922 cls += ' masonry-brick-link';
35925 if(this.bgimage.length){
35926 cls += ' masonry-brick-image';
35929 if(this.maskInverse){
35930 cls += ' mask-inverse';
35933 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35934 cls += ' enable-mask';
35938 cls += ' masonry-' + this.size + '-brick';
35941 if(this.placetitle.length){
35943 switch (this.placetitle) {
35945 cls += ' masonry-center-title';
35948 cls += ' masonry-bottom-title';
35955 if(!this.html.length && !this.bgimage.length){
35956 cls += ' masonry-center-title';
35959 if(!this.html.length && this.bgimage.length){
35960 cls += ' masonry-bottom-title';
35965 cls += ' ' + this.cls;
35969 tag: (this.href.length) ? 'a' : 'div',
35974 cls: 'masonry-brick-mask'
35978 cls: 'masonry-brick-paragraph',
35984 if(this.href.length){
35985 cfg.href = this.href;
35988 var cn = cfg.cn[1].cn;
35990 if(this.title.length){
35993 cls: 'masonry-brick-title',
35998 if(this.html.length){
36001 cls: 'masonry-brick-text',
36006 if (!this.title.length && !this.html.length) {
36007 cfg.cn[1].cls += ' hide';
36010 if(this.bgimage.length){
36013 cls: 'masonry-brick-image-view',
36018 if(this.videourl.length){
36019 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36020 // youtube support only?
36023 cls: 'masonry-brick-image-view',
36026 allowfullscreen : true
36034 getSplitAutoCreate : function()
36036 var cls = 'masonry-brick masonry-brick-split';
36038 if(this.href.length){
36039 cls += ' masonry-brick-link';
36042 if(this.bgimage.length){
36043 cls += ' masonry-brick-image';
36047 cls += ' masonry-' + this.size + '-brick';
36050 switch (this.placetitle) {
36052 cls += ' masonry-center-title';
36055 cls += ' masonry-bottom-title';
36058 if(!this.bgimage.length){
36059 cls += ' masonry-center-title';
36062 if(this.bgimage.length){
36063 cls += ' masonry-bottom-title';
36069 cls += ' ' + this.cls;
36073 tag: (this.href.length) ? 'a' : 'div',
36078 cls: 'masonry-brick-split-head',
36082 cls: 'masonry-brick-paragraph',
36089 cls: 'masonry-brick-split-body',
36095 if(this.href.length){
36096 cfg.href = this.href;
36099 if(this.title.length){
36100 cfg.cn[0].cn[0].cn.push({
36102 cls: 'masonry-brick-title',
36107 if(this.html.length){
36108 cfg.cn[1].cn.push({
36110 cls: 'masonry-brick-text',
36115 if(this.bgimage.length){
36116 cfg.cn[0].cn.push({
36118 cls: 'masonry-brick-image-view',
36123 if(this.videourl.length){
36124 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36125 // youtube support only?
36126 cfg.cn[0].cn.cn.push({
36128 cls: 'masonry-brick-image-view',
36131 allowfullscreen : true
36138 initEvents: function()
36140 switch (this.size) {
36173 this.el.on('touchstart', this.onTouchStart, this);
36174 this.el.on('touchmove', this.onTouchMove, this);
36175 this.el.on('touchend', this.onTouchEnd, this);
36176 this.el.on('contextmenu', this.onContextMenu, this);
36178 this.el.on('mouseenter' ,this.enter, this);
36179 this.el.on('mouseleave', this.leave, this);
36180 this.el.on('click', this.onClick, this);
36183 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36184 this.parent().bricks.push(this);
36189 onClick: function(e, el)
36191 var time = this.endTimer - this.startTimer;
36192 // Roo.log(e.preventDefault());
36195 e.preventDefault();
36200 if(!this.preventDefault){
36204 e.preventDefault();
36206 if (this.activeClass != '') {
36207 this.selectBrick();
36210 this.fireEvent('click', this, e);
36213 enter: function(e, el)
36215 e.preventDefault();
36217 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36221 if(this.bgimage.length && this.html.length){
36222 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36226 leave: function(e, el)
36228 e.preventDefault();
36230 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36234 if(this.bgimage.length && this.html.length){
36235 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36239 onTouchStart: function(e, el)
36241 // e.preventDefault();
36243 this.touchmoved = false;
36245 if(!this.isFitContainer){
36249 if(!this.bgimage.length || !this.html.length){
36253 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36255 this.timer = new Date().getTime();
36259 onTouchMove: function(e, el)
36261 this.touchmoved = true;
36264 onContextMenu : function(e,el)
36266 e.preventDefault();
36267 e.stopPropagation();
36271 onTouchEnd: function(e, el)
36273 // e.preventDefault();
36275 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36282 if(!this.bgimage.length || !this.html.length){
36284 if(this.href.length){
36285 window.location.href = this.href;
36291 if(!this.isFitContainer){
36295 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36297 window.location.href = this.href;
36300 //selection on single brick only
36301 selectBrick : function() {
36303 if (!this.parentId) {
36307 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36308 var index = m.selectedBrick.indexOf(this.id);
36311 m.selectedBrick.splice(index,1);
36312 this.el.removeClass(this.activeClass);
36316 for(var i = 0; i < m.selectedBrick.length; i++) {
36317 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36318 b.el.removeClass(b.activeClass);
36321 m.selectedBrick = [];
36323 m.selectedBrick.push(this.id);
36324 this.el.addClass(this.activeClass);
36328 isSelected : function(){
36329 return this.el.hasClass(this.activeClass);
36334 Roo.apply(Roo.bootstrap.MasonryBrick, {
36337 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36339 * register a Masonry Brick
36340 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36343 register : function(brick)
36345 //this.groups[brick.id] = brick;
36346 this.groups.add(brick.id, brick);
36349 * fetch a masonry brick based on the masonry brick ID
36350 * @param {string} the masonry brick to add
36351 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36354 get: function(brick_id)
36356 // if (typeof(this.groups[brick_id]) == 'undefined') {
36359 // return this.groups[brick_id] ;
36361 if(this.groups.key(brick_id)) {
36362 return this.groups.key(brick_id);
36380 * @class Roo.bootstrap.Brick
36381 * @extends Roo.bootstrap.Component
36382 * Bootstrap Brick class
36385 * Create a new Brick
36386 * @param {Object} config The config object
36389 Roo.bootstrap.Brick = function(config){
36390 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36396 * When a Brick is click
36397 * @param {Roo.bootstrap.Brick} this
36398 * @param {Roo.EventObject} e
36404 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36407 * @cfg {String} title
36411 * @cfg {String} html
36415 * @cfg {String} bgimage
36419 * @cfg {String} cls
36423 * @cfg {String} href
36427 * @cfg {String} video
36431 * @cfg {Boolean} square
36435 getAutoCreate : function()
36437 var cls = 'roo-brick';
36439 if(this.href.length){
36440 cls += ' roo-brick-link';
36443 if(this.bgimage.length){
36444 cls += ' roo-brick-image';
36447 if(!this.html.length && !this.bgimage.length){
36448 cls += ' roo-brick-center-title';
36451 if(!this.html.length && this.bgimage.length){
36452 cls += ' roo-brick-bottom-title';
36456 cls += ' ' + this.cls;
36460 tag: (this.href.length) ? 'a' : 'div',
36465 cls: 'roo-brick-paragraph',
36471 if(this.href.length){
36472 cfg.href = this.href;
36475 var cn = cfg.cn[0].cn;
36477 if(this.title.length){
36480 cls: 'roo-brick-title',
36485 if(this.html.length){
36488 cls: 'roo-brick-text',
36495 if(this.bgimage.length){
36498 cls: 'roo-brick-image-view',
36506 initEvents: function()
36508 if(this.title.length || this.html.length){
36509 this.el.on('mouseenter' ,this.enter, this);
36510 this.el.on('mouseleave', this.leave, this);
36513 Roo.EventManager.onWindowResize(this.resize, this);
36515 if(this.bgimage.length){
36516 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36517 this.imageEl.on('load', this.onImageLoad, this);
36524 onImageLoad : function()
36529 resize : function()
36531 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36533 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36535 if(this.bgimage.length){
36536 var image = this.el.select('.roo-brick-image-view', true).first();
36538 image.setWidth(paragraph.getWidth());
36541 image.setHeight(paragraph.getWidth());
36544 this.el.setHeight(image.getHeight());
36545 paragraph.setHeight(image.getHeight());
36551 enter: function(e, el)
36553 e.preventDefault();
36555 if(this.bgimage.length){
36556 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36557 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36561 leave: function(e, el)
36563 e.preventDefault();
36565 if(this.bgimage.length){
36566 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36567 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36582 * @class Roo.bootstrap.NumberField
36583 * @extends Roo.bootstrap.Input
36584 * Bootstrap NumberField class
36590 * Create a new NumberField
36591 * @param {Object} config The config object
36594 Roo.bootstrap.NumberField = function(config){
36595 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36598 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36601 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36603 allowDecimals : true,
36605 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36607 decimalSeparator : ".",
36609 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36611 decimalPrecision : 2,
36613 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36615 allowNegative : true,
36618 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36622 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36624 minValue : Number.NEGATIVE_INFINITY,
36626 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36628 maxValue : Number.MAX_VALUE,
36630 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36632 minText : "The minimum value for this field is {0}",
36634 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36636 maxText : "The maximum value for this field is {0}",
36638 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36639 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36641 nanText : "{0} is not a valid number",
36643 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36645 thousandsDelimiter : false,
36647 * @cfg {String} valueAlign alignment of value
36649 valueAlign : "left",
36651 getAutoCreate : function()
36653 var hiddenInput = {
36657 cls: 'hidden-number-input'
36661 hiddenInput.name = this.name;
36666 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36668 this.name = hiddenInput.name;
36670 if(cfg.cn.length > 0) {
36671 cfg.cn.push(hiddenInput);
36678 initEvents : function()
36680 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36682 var allowed = "0123456789";
36684 if(this.allowDecimals){
36685 allowed += this.decimalSeparator;
36688 if(this.allowNegative){
36692 if(this.thousandsDelimiter) {
36696 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36698 var keyPress = function(e){
36700 var k = e.getKey();
36702 var c = e.getCharCode();
36705 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36706 allowed.indexOf(String.fromCharCode(c)) === -1
36712 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36716 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36721 this.el.on("keypress", keyPress, this);
36724 validateValue : function(value)
36727 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36731 var num = this.parseValue(value);
36734 this.markInvalid(String.format(this.nanText, value));
36738 if(num < this.minValue){
36739 this.markInvalid(String.format(this.minText, this.minValue));
36743 if(num > this.maxValue){
36744 this.markInvalid(String.format(this.maxText, this.maxValue));
36751 getValue : function()
36753 var v = this.hiddenEl().getValue();
36755 return this.fixPrecision(this.parseValue(v));
36758 parseValue : function(value)
36760 if(this.thousandsDelimiter) {
36762 r = new RegExp(",", "g");
36763 value = value.replace(r, "");
36766 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36767 return isNaN(value) ? '' : value;
36770 fixPrecision : function(value)
36772 if(this.thousandsDelimiter) {
36774 r = new RegExp(",", "g");
36775 value = value.replace(r, "");
36778 var nan = isNaN(value);
36780 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36781 return nan ? '' : value;
36783 return parseFloat(value).toFixed(this.decimalPrecision);
36786 setValue : function(v)
36788 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36794 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36796 this.inputEl().dom.value = (v == '') ? '' :
36797 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36799 if(!this.allowZero && v === '0') {
36800 this.hiddenEl().dom.value = '';
36801 this.inputEl().dom.value = '';
36808 decimalPrecisionFcn : function(v)
36810 return Math.floor(v);
36813 beforeBlur : function()
36815 var v = this.parseValue(this.getRawValue());
36817 if(v || v === 0 || v === ''){
36822 hiddenEl : function()
36824 return this.el.select('input.hidden-number-input',true).first();
36836 * @class Roo.bootstrap.DocumentSlider
36837 * @extends Roo.bootstrap.Component
36838 * Bootstrap DocumentSlider class
36841 * Create a new DocumentViewer
36842 * @param {Object} config The config object
36845 Roo.bootstrap.DocumentSlider = function(config){
36846 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36853 * Fire after initEvent
36854 * @param {Roo.bootstrap.DocumentSlider} this
36859 * Fire after update
36860 * @param {Roo.bootstrap.DocumentSlider} this
36866 * @param {Roo.bootstrap.DocumentSlider} this
36872 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36878 getAutoCreate : function()
36882 cls : 'roo-document-slider',
36886 cls : 'roo-document-slider-header',
36890 cls : 'roo-document-slider-header-title'
36896 cls : 'roo-document-slider-body',
36900 cls : 'roo-document-slider-prev',
36904 cls : 'fa fa-chevron-left'
36910 cls : 'roo-document-slider-thumb',
36914 cls : 'roo-document-slider-image'
36920 cls : 'roo-document-slider-next',
36924 cls : 'fa fa-chevron-right'
36936 initEvents : function()
36938 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36939 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36941 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36942 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36944 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36945 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36947 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36948 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36950 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36951 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36953 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36954 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36956 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36957 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36959 this.thumbEl.on('click', this.onClick, this);
36961 this.prevIndicator.on('click', this.prev, this);
36963 this.nextIndicator.on('click', this.next, this);
36967 initial : function()
36969 if(this.files.length){
36970 this.indicator = 1;
36974 this.fireEvent('initial', this);
36977 update : function()
36979 this.imageEl.attr('src', this.files[this.indicator - 1]);
36981 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36983 this.prevIndicator.show();
36985 if(this.indicator == 1){
36986 this.prevIndicator.hide();
36989 this.nextIndicator.show();
36991 if(this.indicator == this.files.length){
36992 this.nextIndicator.hide();
36995 this.thumbEl.scrollTo('top');
36997 this.fireEvent('update', this);
37000 onClick : function(e)
37002 e.preventDefault();
37004 this.fireEvent('click', this);
37009 e.preventDefault();
37011 this.indicator = Math.max(1, this.indicator - 1);
37018 e.preventDefault();
37020 this.indicator = Math.min(this.files.length, this.indicator + 1);
37034 * @class Roo.bootstrap.RadioSet
37035 * @extends Roo.bootstrap.Input
37036 * @children Roo.bootstrap.Radio
37037 * Bootstrap RadioSet class
37038 * @cfg {String} indicatorpos (left|right) default left
37039 * @cfg {Boolean} inline (true|false) inline the element (default true)
37040 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37042 * Create a new RadioSet
37043 * @param {Object} config The config object
37046 Roo.bootstrap.RadioSet = function(config){
37048 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37052 Roo.bootstrap.RadioSet.register(this);
37057 * Fires when the element is checked or unchecked.
37058 * @param {Roo.bootstrap.RadioSet} this This radio
37059 * @param {Roo.bootstrap.Radio} item The checked item
37064 * Fires when the element is click.
37065 * @param {Roo.bootstrap.RadioSet} this This radio set
37066 * @param {Roo.bootstrap.Radio} item The checked item
37067 * @param {Roo.EventObject} e The event object
37074 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37082 indicatorpos : 'left',
37084 getAutoCreate : function()
37088 cls : 'roo-radio-set-label',
37092 html : this.fieldLabel
37096 if (Roo.bootstrap.version == 3) {
37099 if(this.indicatorpos == 'left'){
37102 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37103 tooltip : 'This field is required'
37108 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37109 tooltip : 'This field is required'
37115 cls : 'roo-radio-set-items'
37118 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37120 if (align === 'left' && this.fieldLabel.length) {
37123 cls : "roo-radio-set-right",
37129 if(this.labelWidth > 12){
37130 label.style = "width: " + this.labelWidth + 'px';
37133 if(this.labelWidth < 13 && this.labelmd == 0){
37134 this.labelmd = this.labelWidth;
37137 if(this.labellg > 0){
37138 label.cls += ' col-lg-' + this.labellg;
37139 items.cls += ' col-lg-' + (12 - this.labellg);
37142 if(this.labelmd > 0){
37143 label.cls += ' col-md-' + this.labelmd;
37144 items.cls += ' col-md-' + (12 - this.labelmd);
37147 if(this.labelsm > 0){
37148 label.cls += ' col-sm-' + this.labelsm;
37149 items.cls += ' col-sm-' + (12 - this.labelsm);
37152 if(this.labelxs > 0){
37153 label.cls += ' col-xs-' + this.labelxs;
37154 items.cls += ' col-xs-' + (12 - this.labelxs);
37160 cls : 'roo-radio-set',
37164 cls : 'roo-radio-set-input',
37167 value : this.value ? this.value : ''
37174 if(this.weight.length){
37175 cfg.cls += ' roo-radio-' + this.weight;
37179 cfg.cls += ' roo-radio-set-inline';
37183 ['xs','sm','md','lg'].map(function(size){
37184 if (settings[size]) {
37185 cfg.cls += ' col-' + size + '-' + settings[size];
37193 initEvents : function()
37195 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37196 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37198 if(!this.fieldLabel.length){
37199 this.labelEl.hide();
37202 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37203 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37205 this.indicator = this.indicatorEl();
37207 if(this.indicator){
37208 this.indicator.addClass('invisible');
37211 this.originalValue = this.getValue();
37215 inputEl: function ()
37217 return this.el.select('.roo-radio-set-input', true).first();
37220 getChildContainer : function()
37222 return this.itemsEl;
37225 register : function(item)
37227 this.radioes.push(item);
37231 validate : function()
37233 if(this.getVisibilityEl().hasClass('hidden')){
37239 Roo.each(this.radioes, function(i){
37248 if(this.allowBlank) {
37252 if(this.disabled || valid){
37257 this.markInvalid();
37262 markValid : function()
37264 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37265 this.indicatorEl().removeClass('visible');
37266 this.indicatorEl().addClass('invisible');
37270 if (Roo.bootstrap.version == 3) {
37271 this.el.removeClass([this.invalidClass, this.validClass]);
37272 this.el.addClass(this.validClass);
37274 this.el.removeClass(['is-invalid','is-valid']);
37275 this.el.addClass(['is-valid']);
37277 this.fireEvent('valid', this);
37280 markInvalid : function(msg)
37282 if(this.allowBlank || this.disabled){
37286 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37287 this.indicatorEl().removeClass('invisible');
37288 this.indicatorEl().addClass('visible');
37290 if (Roo.bootstrap.version == 3) {
37291 this.el.removeClass([this.invalidClass, this.validClass]);
37292 this.el.addClass(this.invalidClass);
37294 this.el.removeClass(['is-invalid','is-valid']);
37295 this.el.addClass(['is-invalid']);
37298 this.fireEvent('invalid', this, msg);
37302 setValue : function(v, suppressEvent)
37304 if(this.value === v){
37311 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37314 Roo.each(this.radioes, function(i){
37316 i.el.removeClass('checked');
37319 Roo.each(this.radioes, function(i){
37321 if(i.value === v || i.value.toString() === v.toString()){
37323 i.el.addClass('checked');
37325 if(suppressEvent !== true){
37326 this.fireEvent('check', this, i);
37337 clearInvalid : function(){
37339 if(!this.el || this.preventMark){
37343 this.el.removeClass([this.invalidClass]);
37345 this.fireEvent('valid', this);
37350 Roo.apply(Roo.bootstrap.RadioSet, {
37354 register : function(set)
37356 this.groups[set.name] = set;
37359 get: function(name)
37361 if (typeof(this.groups[name]) == 'undefined') {
37365 return this.groups[name] ;
37371 * Ext JS Library 1.1.1
37372 * Copyright(c) 2006-2007, Ext JS, LLC.
37374 * Originally Released Under LGPL - original licence link has changed is not relivant.
37377 * <script type="text/javascript">
37382 * @class Roo.bootstrap.SplitBar
37383 * @extends Roo.util.Observable
37384 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37388 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37389 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37390 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37391 split.minSize = 100;
37392 split.maxSize = 600;
37393 split.animate = true;
37394 split.on('moved', splitterMoved);
37397 * Create a new SplitBar
37398 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37399 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37400 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37401 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37402 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37403 position of the SplitBar).
37405 Roo.bootstrap.SplitBar = function(cfg){
37410 // dragElement : elm
37411 // resizingElement: el,
37413 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37414 // placement : Roo.bootstrap.SplitBar.LEFT ,
37415 // existingProxy ???
37418 this.el = Roo.get(cfg.dragElement, true);
37419 this.el.dom.unselectable = "on";
37421 this.resizingEl = Roo.get(cfg.resizingElement, true);
37425 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37426 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37429 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37432 * The minimum size of the resizing element. (Defaults to 0)
37438 * The maximum size of the resizing element. (Defaults to 2000)
37441 this.maxSize = 2000;
37444 * Whether to animate the transition to the new size
37447 this.animate = false;
37450 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37453 this.useShim = false;
37458 if(!cfg.existingProxy){
37460 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37462 this.proxy = Roo.get(cfg.existingProxy).dom;
37465 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37468 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37471 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37474 this.dragSpecs = {};
37477 * @private The adapter to use to positon and resize elements
37479 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37480 this.adapter.init(this);
37482 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37484 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37485 this.el.addClass("roo-splitbar-h");
37488 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37489 this.el.addClass("roo-splitbar-v");
37495 * Fires when the splitter is moved (alias for {@link #event-moved})
37496 * @param {Roo.bootstrap.SplitBar} this
37497 * @param {Number} newSize the new width or height
37502 * Fires when the splitter is moved
37503 * @param {Roo.bootstrap.SplitBar} this
37504 * @param {Number} newSize the new width or height
37508 * @event beforeresize
37509 * Fires before the splitter is dragged
37510 * @param {Roo.bootstrap.SplitBar} this
37512 "beforeresize" : true,
37514 "beforeapply" : true
37517 Roo.util.Observable.call(this);
37520 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37521 onStartProxyDrag : function(x, y){
37522 this.fireEvent("beforeresize", this);
37524 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37526 o.enableDisplayMode("block");
37527 // all splitbars share the same overlay
37528 Roo.bootstrap.SplitBar.prototype.overlay = o;
37530 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37531 this.overlay.show();
37532 Roo.get(this.proxy).setDisplayed("block");
37533 var size = this.adapter.getElementSize(this);
37534 this.activeMinSize = this.getMinimumSize();;
37535 this.activeMaxSize = this.getMaximumSize();;
37536 var c1 = size - this.activeMinSize;
37537 var c2 = Math.max(this.activeMaxSize - size, 0);
37538 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37539 this.dd.resetConstraints();
37540 this.dd.setXConstraint(
37541 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37542 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37544 this.dd.setYConstraint(0, 0);
37546 this.dd.resetConstraints();
37547 this.dd.setXConstraint(0, 0);
37548 this.dd.setYConstraint(
37549 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37550 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37553 this.dragSpecs.startSize = size;
37554 this.dragSpecs.startPoint = [x, y];
37555 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37559 * @private Called after the drag operation by the DDProxy
37561 onEndProxyDrag : function(e){
37562 Roo.get(this.proxy).setDisplayed(false);
37563 var endPoint = Roo.lib.Event.getXY(e);
37565 this.overlay.hide();
37568 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37569 newSize = this.dragSpecs.startSize +
37570 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37571 endPoint[0] - this.dragSpecs.startPoint[0] :
37572 this.dragSpecs.startPoint[0] - endPoint[0]
37575 newSize = this.dragSpecs.startSize +
37576 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37577 endPoint[1] - this.dragSpecs.startPoint[1] :
37578 this.dragSpecs.startPoint[1] - endPoint[1]
37581 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37582 if(newSize != this.dragSpecs.startSize){
37583 if(this.fireEvent('beforeapply', this, newSize) !== false){
37584 this.adapter.setElementSize(this, newSize);
37585 this.fireEvent("moved", this, newSize);
37586 this.fireEvent("resize", this, newSize);
37592 * Get the adapter this SplitBar uses
37593 * @return The adapter object
37595 getAdapter : function(){
37596 return this.adapter;
37600 * Set the adapter this SplitBar uses
37601 * @param {Object} adapter A SplitBar adapter object
37603 setAdapter : function(adapter){
37604 this.adapter = adapter;
37605 this.adapter.init(this);
37609 * Gets the minimum size for the resizing element
37610 * @return {Number} The minimum size
37612 getMinimumSize : function(){
37613 return this.minSize;
37617 * Sets the minimum size for the resizing element
37618 * @param {Number} minSize The minimum size
37620 setMinimumSize : function(minSize){
37621 this.minSize = minSize;
37625 * Gets the maximum size for the resizing element
37626 * @return {Number} The maximum size
37628 getMaximumSize : function(){
37629 return this.maxSize;
37633 * Sets the maximum size for the resizing element
37634 * @param {Number} maxSize The maximum size
37636 setMaximumSize : function(maxSize){
37637 this.maxSize = maxSize;
37641 * Sets the initialize size for the resizing element
37642 * @param {Number} size The initial size
37644 setCurrentSize : function(size){
37645 var oldAnimate = this.animate;
37646 this.animate = false;
37647 this.adapter.setElementSize(this, size);
37648 this.animate = oldAnimate;
37652 * Destroy this splitbar.
37653 * @param {Boolean} removeEl True to remove the element
37655 destroy : function(removeEl){
37657 this.shim.remove();
37660 this.proxy.parentNode.removeChild(this.proxy);
37668 * @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.
37670 Roo.bootstrap.SplitBar.createProxy = function(dir){
37671 var proxy = new Roo.Element(document.createElement("div"));
37672 proxy.unselectable();
37673 var cls = 'roo-splitbar-proxy';
37674 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37675 document.body.appendChild(proxy.dom);
37680 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37681 * Default Adapter. It assumes the splitter and resizing element are not positioned
37682 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37684 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37687 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37688 // do nothing for now
37689 init : function(s){
37693 * Called before drag operations to get the current size of the resizing element.
37694 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37696 getElementSize : function(s){
37697 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37698 return s.resizingEl.getWidth();
37700 return s.resizingEl.getHeight();
37705 * Called after drag operations to set the size of the resizing element.
37706 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37707 * @param {Number} newSize The new size to set
37708 * @param {Function} onComplete A function to be invoked when resizing is complete
37710 setElementSize : function(s, newSize, onComplete){
37711 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37713 s.resizingEl.setWidth(newSize);
37715 onComplete(s, newSize);
37718 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37723 s.resizingEl.setHeight(newSize);
37725 onComplete(s, newSize);
37728 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37735 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37736 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37737 * Adapter that moves the splitter element to align with the resized sizing element.
37738 * Used with an absolute positioned SplitBar.
37739 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37740 * document.body, make sure you assign an id to the body element.
37742 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37743 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37744 this.container = Roo.get(container);
37747 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37748 init : function(s){
37749 this.basic.init(s);
37752 getElementSize : function(s){
37753 return this.basic.getElementSize(s);
37756 setElementSize : function(s, newSize, onComplete){
37757 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37760 moveSplitter : function(s){
37761 var yes = Roo.bootstrap.SplitBar;
37762 switch(s.placement){
37764 s.el.setX(s.resizingEl.getRight());
37767 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37770 s.el.setY(s.resizingEl.getBottom());
37773 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37780 * Orientation constant - Create a vertical SplitBar
37784 Roo.bootstrap.SplitBar.VERTICAL = 1;
37787 * Orientation constant - Create a horizontal SplitBar
37791 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37794 * Placement constant - The resizing element is to the left of the splitter element
37798 Roo.bootstrap.SplitBar.LEFT = 1;
37801 * Placement constant - The resizing element is to the right of the splitter element
37805 Roo.bootstrap.SplitBar.RIGHT = 2;
37808 * Placement constant - The resizing element is positioned above the splitter element
37812 Roo.bootstrap.SplitBar.TOP = 3;
37815 * Placement constant - The resizing element is positioned under splitter element
37819 Roo.bootstrap.SplitBar.BOTTOM = 4;
37820 Roo.namespace("Roo.bootstrap.layout");/*
37822 * Ext JS Library 1.1.1
37823 * Copyright(c) 2006-2007, Ext JS, LLC.
37825 * Originally Released Under LGPL - original licence link has changed is not relivant.
37828 * <script type="text/javascript">
37832 * @class Roo.bootstrap.layout.Manager
37833 * @extends Roo.bootstrap.Component
37834 * Base class for layout managers.
37836 Roo.bootstrap.layout.Manager = function(config)
37838 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37844 /** false to disable window resize monitoring @type Boolean */
37845 this.monitorWindowResize = true;
37850 * Fires when a layout is performed.
37851 * @param {Roo.LayoutManager} this
37855 * @event regionresized
37856 * Fires when the user resizes a region.
37857 * @param {Roo.LayoutRegion} region The resized region
37858 * @param {Number} newSize The new size (width for east/west, height for north/south)
37860 "regionresized" : true,
37862 * @event regioncollapsed
37863 * Fires when a region is collapsed.
37864 * @param {Roo.LayoutRegion} region The collapsed region
37866 "regioncollapsed" : true,
37868 * @event regionexpanded
37869 * Fires when a region is expanded.
37870 * @param {Roo.LayoutRegion} region The expanded region
37872 "regionexpanded" : true
37874 this.updating = false;
37877 this.el = Roo.get(config.el);
37883 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37888 monitorWindowResize : true,
37894 onRender : function(ct, position)
37897 this.el = Roo.get(ct);
37900 //this.fireEvent('render',this);
37904 initEvents: function()
37908 // ie scrollbar fix
37909 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37910 document.body.scroll = "no";
37911 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37912 this.el.position('relative');
37914 this.id = this.el.id;
37915 this.el.addClass("roo-layout-container");
37916 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37917 if(this.el.dom != document.body ) {
37918 this.el.on('resize', this.layout,this);
37919 this.el.on('show', this.layout,this);
37925 * Returns true if this layout is currently being updated
37926 * @return {Boolean}
37928 isUpdating : function(){
37929 return this.updating;
37933 * Suspend the LayoutManager from doing auto-layouts while
37934 * making multiple add or remove calls
37936 beginUpdate : function(){
37937 this.updating = true;
37941 * Restore auto-layouts and optionally disable the manager from performing a layout
37942 * @param {Boolean} noLayout true to disable a layout update
37944 endUpdate : function(noLayout){
37945 this.updating = false;
37951 layout: function(){
37955 onRegionResized : function(region, newSize){
37956 this.fireEvent("regionresized", region, newSize);
37960 onRegionCollapsed : function(region){
37961 this.fireEvent("regioncollapsed", region);
37964 onRegionExpanded : function(region){
37965 this.fireEvent("regionexpanded", region);
37969 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37970 * performs box-model adjustments.
37971 * @return {Object} The size as an object {width: (the width), height: (the height)}
37973 getViewSize : function()
37976 if(this.el.dom != document.body){
37977 size = this.el.getSize();
37979 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37981 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37982 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37987 * Returns the Element this layout is bound to.
37988 * @return {Roo.Element}
37990 getEl : function(){
37995 * Returns the specified region.
37996 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37997 * @return {Roo.LayoutRegion}
37999 getRegion : function(target){
38000 return this.regions[target.toLowerCase()];
38003 onWindowResize : function(){
38004 if(this.monitorWindowResize){
38011 * Ext JS Library 1.1.1
38012 * Copyright(c) 2006-2007, Ext JS, LLC.
38014 * Originally Released Under LGPL - original licence link has changed is not relivant.
38017 * <script type="text/javascript">
38020 * @class Roo.bootstrap.layout.Border
38021 * @extends Roo.bootstrap.layout.Manager
38023 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38024 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38025 * please see: examples/bootstrap/nested.html<br><br>
38027 <b>The container the layout is rendered into can be either the body element or any other element.
38028 If it is not the body element, the container needs to either be an absolute positioned element,
38029 or you will need to add "position:relative" to the css of the container. You will also need to specify
38030 the container size if it is not the body element.</b>
38033 * Create a new Border
38034 * @param {Object} config Configuration options
38036 Roo.bootstrap.layout.Border = function(config){
38037 config = config || {};
38038 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38042 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38043 if(config[region]){
38044 config[region].region = region;
38045 this.addRegion(config[region]);
38051 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38053 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38056 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38059 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38062 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38065 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38068 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38074 parent : false, // this might point to a 'nest' or a ???
38077 * Creates and adds a new region if it doesn't already exist.
38078 * @param {String} target The target region key (north, south, east, west or center).
38079 * @param {Object} config The regions config object
38080 * @return {BorderLayoutRegion} The new region
38082 addRegion : function(config)
38084 if(!this.regions[config.region]){
38085 var r = this.factory(config);
38086 this.bindRegion(r);
38088 return this.regions[config.region];
38092 bindRegion : function(r){
38093 this.regions[r.config.region] = r;
38095 r.on("visibilitychange", this.layout, this);
38096 r.on("paneladded", this.layout, this);
38097 r.on("panelremoved", this.layout, this);
38098 r.on("invalidated", this.layout, this);
38099 r.on("resized", this.onRegionResized, this);
38100 r.on("collapsed", this.onRegionCollapsed, this);
38101 r.on("expanded", this.onRegionExpanded, this);
38105 * Performs a layout update.
38107 layout : function()
38109 if(this.updating) {
38113 // render all the rebions if they have not been done alreayd?
38114 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38115 if(this.regions[region] && !this.regions[region].bodyEl){
38116 this.regions[region].onRender(this.el)
38120 var size = this.getViewSize();
38121 var w = size.width;
38122 var h = size.height;
38127 //var x = 0, y = 0;
38129 var rs = this.regions;
38130 var north = rs["north"];
38131 var south = rs["south"];
38132 var west = rs["west"];
38133 var east = rs["east"];
38134 var center = rs["center"];
38135 //if(this.hideOnLayout){ // not supported anymore
38136 //c.el.setStyle("display", "none");
38138 if(north && north.isVisible()){
38139 var b = north.getBox();
38140 var m = north.getMargins();
38141 b.width = w - (m.left+m.right);
38144 centerY = b.height + b.y + m.bottom;
38145 centerH -= centerY;
38146 north.updateBox(this.safeBox(b));
38148 if(south && south.isVisible()){
38149 var b = south.getBox();
38150 var m = south.getMargins();
38151 b.width = w - (m.left+m.right);
38153 var totalHeight = (b.height + m.top + m.bottom);
38154 b.y = h - totalHeight + m.top;
38155 centerH -= totalHeight;
38156 south.updateBox(this.safeBox(b));
38158 if(west && west.isVisible()){
38159 var b = west.getBox();
38160 var m = west.getMargins();
38161 b.height = centerH - (m.top+m.bottom);
38163 b.y = centerY + m.top;
38164 var totalWidth = (b.width + m.left + m.right);
38165 centerX += totalWidth;
38166 centerW -= totalWidth;
38167 west.updateBox(this.safeBox(b));
38169 if(east && east.isVisible()){
38170 var b = east.getBox();
38171 var m = east.getMargins();
38172 b.height = centerH - (m.top+m.bottom);
38173 var totalWidth = (b.width + m.left + m.right);
38174 b.x = w - totalWidth + m.left;
38175 b.y = centerY + m.top;
38176 centerW -= totalWidth;
38177 east.updateBox(this.safeBox(b));
38180 var m = center.getMargins();
38182 x: centerX + m.left,
38183 y: centerY + m.top,
38184 width: centerW - (m.left+m.right),
38185 height: centerH - (m.top+m.bottom)
38187 //if(this.hideOnLayout){
38188 //center.el.setStyle("display", "block");
38190 center.updateBox(this.safeBox(centerBox));
38193 this.fireEvent("layout", this);
38197 safeBox : function(box){
38198 box.width = Math.max(0, box.width);
38199 box.height = Math.max(0, box.height);
38204 * Adds a ContentPanel (or subclass) to this layout.
38205 * @param {String} target The target region key (north, south, east, west or center).
38206 * @param {Roo.ContentPanel} panel The panel to add
38207 * @return {Roo.ContentPanel} The added panel
38209 add : function(target, panel){
38211 target = target.toLowerCase();
38212 return this.regions[target].add(panel);
38216 * Remove a ContentPanel (or subclass) to this layout.
38217 * @param {String} target The target region key (north, south, east, west or center).
38218 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38219 * @return {Roo.ContentPanel} The removed panel
38221 remove : function(target, panel){
38222 target = target.toLowerCase();
38223 return this.regions[target].remove(panel);
38227 * Searches all regions for a panel with the specified id
38228 * @param {String} panelId
38229 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38231 findPanel : function(panelId){
38232 var rs = this.regions;
38233 for(var target in rs){
38234 if(typeof rs[target] != "function"){
38235 var p = rs[target].getPanel(panelId);
38245 * Searches all regions for a panel with the specified id and activates (shows) it.
38246 * @param {String/ContentPanel} panelId The panels id or the panel itself
38247 * @return {Roo.ContentPanel} The shown panel or null
38249 showPanel : function(panelId) {
38250 var rs = this.regions;
38251 for(var target in rs){
38252 var r = rs[target];
38253 if(typeof r != "function"){
38254 if(r.hasPanel(panelId)){
38255 return r.showPanel(panelId);
38263 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38264 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38267 restoreState : function(provider){
38269 provider = Roo.state.Manager;
38271 var sm = new Roo.LayoutStateManager();
38272 sm.init(this, provider);
38278 * Adds a xtype elements to the layout.
38282 xtype : 'ContentPanel',
38289 xtype : 'NestedLayoutPanel',
38295 items : [ ... list of content panels or nested layout panels.. ]
38299 * @param {Object} cfg Xtype definition of item to add.
38301 addxtype : function(cfg)
38303 // basically accepts a pannel...
38304 // can accept a layout region..!?!?
38305 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38308 // theory? children can only be panels??
38310 //if (!cfg.xtype.match(/Panel$/)) {
38315 if (typeof(cfg.region) == 'undefined') {
38316 Roo.log("Failed to add Panel, region was not set");
38320 var region = cfg.region;
38326 xitems = cfg.items;
38331 if ( region == 'center') {
38332 Roo.log("Center: " + cfg.title);
38338 case 'Content': // ContentPanel (el, cfg)
38339 case 'Scroll': // ContentPanel (el, cfg)
38341 cfg.autoCreate = cfg.autoCreate || true;
38342 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38344 // var el = this.el.createChild();
38345 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38348 this.add(region, ret);
38352 case 'TreePanel': // our new panel!
38353 cfg.el = this.el.createChild();
38354 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38355 this.add(region, ret);
38360 // create a new Layout (which is a Border Layout...
38362 var clayout = cfg.layout;
38363 clayout.el = this.el.createChild();
38364 clayout.items = clayout.items || [];
38368 // replace this exitems with the clayout ones..
38369 xitems = clayout.items;
38371 // force background off if it's in center...
38372 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38373 cfg.background = false;
38375 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38378 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38379 //console.log('adding nested layout panel ' + cfg.toSource());
38380 this.add(region, ret);
38381 nb = {}; /// find first...
38386 // needs grid and region
38388 //var el = this.getRegion(region).el.createChild();
38390 *var el = this.el.createChild();
38391 // create the grid first...
38392 cfg.grid.container = el;
38393 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38396 if (region == 'center' && this.active ) {
38397 cfg.background = false;
38400 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38402 this.add(region, ret);
38404 if (cfg.background) {
38405 // render grid on panel activation (if panel background)
38406 ret.on('activate', function(gp) {
38407 if (!gp.grid.rendered) {
38408 // gp.grid.render(el);
38412 // cfg.grid.render(el);
38418 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38419 // it was the old xcomponent building that caused this before.
38420 // espeically if border is the top element in the tree.
38430 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38432 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38433 this.add(region, ret);
38437 throw "Can not add '" + cfg.xtype + "' to Border";
38443 this.beginUpdate();
38447 Roo.each(xitems, function(i) {
38448 region = nb && i.region ? i.region : false;
38450 var add = ret.addxtype(i);
38453 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38454 if (!i.background) {
38455 abn[region] = nb[region] ;
38462 // make the last non-background panel active..
38463 //if (nb) { Roo.log(abn); }
38466 for(var r in abn) {
38467 region = this.getRegion(r);
38469 // tried using nb[r], but it does not work..
38471 region.showPanel(abn[r]);
38482 factory : function(cfg)
38485 var validRegions = Roo.bootstrap.layout.Border.regions;
38487 var target = cfg.region;
38490 var r = Roo.bootstrap.layout;
38494 return new r.North(cfg);
38496 return new r.South(cfg);
38498 return new r.East(cfg);
38500 return new r.West(cfg);
38502 return new r.Center(cfg);
38504 throw 'Layout region "'+target+'" not supported.';
38511 * Ext JS Library 1.1.1
38512 * Copyright(c) 2006-2007, Ext JS, LLC.
38514 * Originally Released Under LGPL - original licence link has changed is not relivant.
38517 * <script type="text/javascript">
38521 * @class Roo.bootstrap.layout.Basic
38522 * @extends Roo.util.Observable
38523 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38524 * and does not have a titlebar, tabs or any other features. All it does is size and position
38525 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38526 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38527 * @cfg {string} region the region that it inhabits..
38528 * @cfg {bool} skipConfig skip config?
38532 Roo.bootstrap.layout.Basic = function(config){
38534 this.mgr = config.mgr;
38536 this.position = config.region;
38538 var skipConfig = config.skipConfig;
38542 * @scope Roo.BasicLayoutRegion
38546 * @event beforeremove
38547 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38548 * @param {Roo.LayoutRegion} this
38549 * @param {Roo.ContentPanel} panel The panel
38550 * @param {Object} e The cancel event object
38552 "beforeremove" : true,
38554 * @event invalidated
38555 * Fires when the layout for this region is changed.
38556 * @param {Roo.LayoutRegion} this
38558 "invalidated" : true,
38560 * @event visibilitychange
38561 * Fires when this region is shown or hidden
38562 * @param {Roo.LayoutRegion} this
38563 * @param {Boolean} visibility true or false
38565 "visibilitychange" : true,
38567 * @event paneladded
38568 * Fires when a panel is added.
38569 * @param {Roo.LayoutRegion} this
38570 * @param {Roo.ContentPanel} panel The panel
38572 "paneladded" : true,
38574 * @event panelremoved
38575 * Fires when a panel is removed.
38576 * @param {Roo.LayoutRegion} this
38577 * @param {Roo.ContentPanel} panel The panel
38579 "panelremoved" : true,
38581 * @event beforecollapse
38582 * Fires when this region before collapse.
38583 * @param {Roo.LayoutRegion} this
38585 "beforecollapse" : true,
38588 * Fires when this region is collapsed.
38589 * @param {Roo.LayoutRegion} this
38591 "collapsed" : true,
38594 * Fires when this region is expanded.
38595 * @param {Roo.LayoutRegion} this
38600 * Fires when this region is slid into view.
38601 * @param {Roo.LayoutRegion} this
38603 "slideshow" : true,
38606 * Fires when this region slides out of view.
38607 * @param {Roo.LayoutRegion} this
38609 "slidehide" : true,
38611 * @event panelactivated
38612 * Fires when a panel is activated.
38613 * @param {Roo.LayoutRegion} this
38614 * @param {Roo.ContentPanel} panel The activated panel
38616 "panelactivated" : true,
38619 * Fires when the user resizes this region.
38620 * @param {Roo.LayoutRegion} this
38621 * @param {Number} newSize The new size (width for east/west, height for north/south)
38625 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38626 this.panels = new Roo.util.MixedCollection();
38627 this.panels.getKey = this.getPanelId.createDelegate(this);
38629 this.activePanel = null;
38630 // ensure listeners are added...
38632 if (config.listeners || config.events) {
38633 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38634 listeners : config.listeners || {},
38635 events : config.events || {}
38639 if(skipConfig !== true){
38640 this.applyConfig(config);
38644 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38646 getPanelId : function(p){
38650 applyConfig : function(config){
38651 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38652 this.config = config;
38657 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38658 * the width, for horizontal (north, south) the height.
38659 * @param {Number} newSize The new width or height
38661 resizeTo : function(newSize){
38662 var el = this.el ? this.el :
38663 (this.activePanel ? this.activePanel.getEl() : null);
38665 switch(this.position){
38668 el.setWidth(newSize);
38669 this.fireEvent("resized", this, newSize);
38673 el.setHeight(newSize);
38674 this.fireEvent("resized", this, newSize);
38680 getBox : function(){
38681 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38684 getMargins : function(){
38685 return this.margins;
38688 updateBox : function(box){
38690 var el = this.activePanel.getEl();
38691 el.dom.style.left = box.x + "px";
38692 el.dom.style.top = box.y + "px";
38693 this.activePanel.setSize(box.width, box.height);
38697 * Returns the container element for this region.
38698 * @return {Roo.Element}
38700 getEl : function(){
38701 return this.activePanel;
38705 * Returns true if this region is currently visible.
38706 * @return {Boolean}
38708 isVisible : function(){
38709 return this.activePanel ? true : false;
38712 setActivePanel : function(panel){
38713 panel = this.getPanel(panel);
38714 if(this.activePanel && this.activePanel != panel){
38715 this.activePanel.setActiveState(false);
38716 this.activePanel.getEl().setLeftTop(-10000,-10000);
38718 this.activePanel = panel;
38719 panel.setActiveState(true);
38721 panel.setSize(this.box.width, this.box.height);
38723 this.fireEvent("panelactivated", this, panel);
38724 this.fireEvent("invalidated");
38728 * Show the specified panel.
38729 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38730 * @return {Roo.ContentPanel} The shown panel or null
38732 showPanel : function(panel){
38733 panel = this.getPanel(panel);
38735 this.setActivePanel(panel);
38741 * Get the active panel for this region.
38742 * @return {Roo.ContentPanel} The active panel or null
38744 getActivePanel : function(){
38745 return this.activePanel;
38749 * Add the passed ContentPanel(s)
38750 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38751 * @return {Roo.ContentPanel} The panel added (if only one was added)
38753 add : function(panel){
38754 if(arguments.length > 1){
38755 for(var i = 0, len = arguments.length; i < len; i++) {
38756 this.add(arguments[i]);
38760 if(this.hasPanel(panel)){
38761 this.showPanel(panel);
38764 var el = panel.getEl();
38765 if(el.dom.parentNode != this.mgr.el.dom){
38766 this.mgr.el.dom.appendChild(el.dom);
38768 if(panel.setRegion){
38769 panel.setRegion(this);
38771 this.panels.add(panel);
38772 el.setStyle("position", "absolute");
38773 if(!panel.background){
38774 this.setActivePanel(panel);
38775 if(this.config.initialSize && this.panels.getCount()==1){
38776 this.resizeTo(this.config.initialSize);
38779 this.fireEvent("paneladded", this, panel);
38784 * Returns true if the panel is in this region.
38785 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38786 * @return {Boolean}
38788 hasPanel : function(panel){
38789 if(typeof panel == "object"){ // must be panel obj
38790 panel = panel.getId();
38792 return this.getPanel(panel) ? true : false;
38796 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38797 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38798 * @param {Boolean} preservePanel Overrides the config preservePanel option
38799 * @return {Roo.ContentPanel} The panel that was removed
38801 remove : function(panel, preservePanel){
38802 panel = this.getPanel(panel);
38807 this.fireEvent("beforeremove", this, panel, e);
38808 if(e.cancel === true){
38811 var panelId = panel.getId();
38812 this.panels.removeKey(panelId);
38817 * Returns the panel specified or null if it's not in this region.
38818 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38819 * @return {Roo.ContentPanel}
38821 getPanel : function(id){
38822 if(typeof id == "object"){ // must be panel obj
38825 return this.panels.get(id);
38829 * Returns this regions position (north/south/east/west/center).
38832 getPosition: function(){
38833 return this.position;
38837 * Ext JS Library 1.1.1
38838 * Copyright(c) 2006-2007, Ext JS, LLC.
38840 * Originally Released Under LGPL - original licence link has changed is not relivant.
38843 * <script type="text/javascript">
38847 * @class Roo.bootstrap.layout.Region
38848 * @extends Roo.bootstrap.layout.Basic
38849 * This class represents a region in a layout manager.
38851 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38852 * @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})
38853 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38854 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38855 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38856 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38857 * @cfg {String} title The title for the region (overrides panel titles)
38858 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38859 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38860 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38861 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38862 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38863 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38864 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38865 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38866 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38867 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38869 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38870 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38871 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38872 * @cfg {Number} width For East/West panels
38873 * @cfg {Number} height For North/South panels
38874 * @cfg {Boolean} split To show the splitter
38875 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38877 * @cfg {string} cls Extra CSS classes to add to region
38879 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38880 * @cfg {string} region the region that it inhabits..
38883 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38884 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38886 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38887 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38888 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38890 Roo.bootstrap.layout.Region = function(config)
38892 this.applyConfig(config);
38894 var mgr = config.mgr;
38895 var pos = config.region;
38896 config.skipConfig = true;
38897 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38900 this.onRender(mgr.el);
38903 this.visible = true;
38904 this.collapsed = false;
38905 this.unrendered_panels = [];
38908 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38910 position: '', // set by wrapper (eg. north/south etc..)
38911 unrendered_panels : null, // unrendered panels.
38913 tabPosition : false,
38915 mgr: false, // points to 'Border'
38918 createBody : function(){
38919 /** This region's body element
38920 * @type Roo.Element */
38921 this.bodyEl = this.el.createChild({
38923 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38927 onRender: function(ctr, pos)
38929 var dh = Roo.DomHelper;
38930 /** This region's container element
38931 * @type Roo.Element */
38932 this.el = dh.append(ctr.dom, {
38934 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38936 /** This region's title element
38937 * @type Roo.Element */
38939 this.titleEl = dh.append(this.el.dom, {
38941 unselectable: "on",
38942 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38944 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38945 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38949 this.titleEl.enableDisplayMode();
38950 /** This region's title text element
38951 * @type HTMLElement */
38952 this.titleTextEl = this.titleEl.dom.firstChild;
38953 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38955 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38956 this.closeBtn.enableDisplayMode();
38957 this.closeBtn.on("click", this.closeClicked, this);
38958 this.closeBtn.hide();
38960 this.createBody(this.config);
38961 if(this.config.hideWhenEmpty){
38963 this.on("paneladded", this.validateVisibility, this);
38964 this.on("panelremoved", this.validateVisibility, this);
38966 if(this.autoScroll){
38967 this.bodyEl.setStyle("overflow", "auto");
38969 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38971 //if(c.titlebar !== false){
38972 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38973 this.titleEl.hide();
38975 this.titleEl.show();
38976 if(this.config.title){
38977 this.titleTextEl.innerHTML = this.config.title;
38981 if(this.config.collapsed){
38982 this.collapse(true);
38984 if(this.config.hidden){
38988 if (this.unrendered_panels && this.unrendered_panels.length) {
38989 for (var i =0;i< this.unrendered_panels.length; i++) {
38990 this.add(this.unrendered_panels[i]);
38992 this.unrendered_panels = null;
38998 applyConfig : function(c)
39001 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39002 var dh = Roo.DomHelper;
39003 if(c.titlebar !== false){
39004 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39005 this.collapseBtn.on("click", this.collapse, this);
39006 this.collapseBtn.enableDisplayMode();
39008 if(c.showPin === true || this.showPin){
39009 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39010 this.stickBtn.enableDisplayMode();
39011 this.stickBtn.on("click", this.expand, this);
39012 this.stickBtn.hide();
39017 /** This region's collapsed element
39018 * @type Roo.Element */
39021 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39022 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39025 if(c.floatable !== false){
39026 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39027 this.collapsedEl.on("click", this.collapseClick, this);
39030 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39031 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39032 id: "message", unselectable: "on", style:{"float":"left"}});
39033 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39035 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39036 this.expandBtn.on("click", this.expand, this);
39040 if(this.collapseBtn){
39041 this.collapseBtn.setVisible(c.collapsible == true);
39044 this.cmargins = c.cmargins || this.cmargins ||
39045 (this.position == "west" || this.position == "east" ?
39046 {top: 0, left: 2, right:2, bottom: 0} :
39047 {top: 2, left: 0, right:0, bottom: 2});
39049 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39052 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39054 this.autoScroll = c.autoScroll || false;
39059 this.duration = c.duration || .30;
39060 this.slideDuration = c.slideDuration || .45;
39065 * Returns true if this region is currently visible.
39066 * @return {Boolean}
39068 isVisible : function(){
39069 return this.visible;
39073 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39074 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39076 //setCollapsedTitle : function(title){
39077 // title = title || " ";
39078 // if(this.collapsedTitleTextEl){
39079 // this.collapsedTitleTextEl.innerHTML = title;
39083 getBox : function(){
39085 // if(!this.collapsed){
39086 b = this.el.getBox(false, true);
39088 // b = this.collapsedEl.getBox(false, true);
39093 getMargins : function(){
39094 return this.margins;
39095 //return this.collapsed ? this.cmargins : this.margins;
39098 highlight : function(){
39099 this.el.addClass("x-layout-panel-dragover");
39102 unhighlight : function(){
39103 this.el.removeClass("x-layout-panel-dragover");
39106 updateBox : function(box)
39108 if (!this.bodyEl) {
39109 return; // not rendered yet..
39113 if(!this.collapsed){
39114 this.el.dom.style.left = box.x + "px";
39115 this.el.dom.style.top = box.y + "px";
39116 this.updateBody(box.width, box.height);
39118 this.collapsedEl.dom.style.left = box.x + "px";
39119 this.collapsedEl.dom.style.top = box.y + "px";
39120 this.collapsedEl.setSize(box.width, box.height);
39123 this.tabs.autoSizeTabs();
39127 updateBody : function(w, h)
39130 this.el.setWidth(w);
39131 w -= this.el.getBorderWidth("rl");
39132 if(this.config.adjustments){
39133 w += this.config.adjustments[0];
39136 if(h !== null && h > 0){
39137 this.el.setHeight(h);
39138 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39139 h -= this.el.getBorderWidth("tb");
39140 if(this.config.adjustments){
39141 h += this.config.adjustments[1];
39143 this.bodyEl.setHeight(h);
39145 h = this.tabs.syncHeight(h);
39148 if(this.panelSize){
39149 w = w !== null ? w : this.panelSize.width;
39150 h = h !== null ? h : this.panelSize.height;
39152 if(this.activePanel){
39153 var el = this.activePanel.getEl();
39154 w = w !== null ? w : el.getWidth();
39155 h = h !== null ? h : el.getHeight();
39156 this.panelSize = {width: w, height: h};
39157 this.activePanel.setSize(w, h);
39159 if(Roo.isIE && this.tabs){
39160 this.tabs.el.repaint();
39165 * Returns the container element for this region.
39166 * @return {Roo.Element}
39168 getEl : function(){
39173 * Hides this region.
39176 //if(!this.collapsed){
39177 this.el.dom.style.left = "-2000px";
39180 // this.collapsedEl.dom.style.left = "-2000px";
39181 // this.collapsedEl.hide();
39183 this.visible = false;
39184 this.fireEvent("visibilitychange", this, false);
39188 * Shows this region if it was previously hidden.
39191 //if(!this.collapsed){
39194 // this.collapsedEl.show();
39196 this.visible = true;
39197 this.fireEvent("visibilitychange", this, true);
39200 closeClicked : function(){
39201 if(this.activePanel){
39202 this.remove(this.activePanel);
39206 collapseClick : function(e){
39208 e.stopPropagation();
39211 e.stopPropagation();
39217 * Collapses this region.
39218 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39221 collapse : function(skipAnim, skipCheck = false){
39222 if(this.collapsed) {
39226 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39228 this.collapsed = true;
39230 this.split.el.hide();
39232 if(this.config.animate && skipAnim !== true){
39233 this.fireEvent("invalidated", this);
39234 this.animateCollapse();
39236 this.el.setLocation(-20000,-20000);
39238 this.collapsedEl.show();
39239 this.fireEvent("collapsed", this);
39240 this.fireEvent("invalidated", this);
39246 animateCollapse : function(){
39251 * Expands this region if it was previously collapsed.
39252 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39253 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39256 expand : function(e, skipAnim){
39258 e.stopPropagation();
39260 if(!this.collapsed || this.el.hasActiveFx()) {
39264 this.afterSlideIn();
39267 this.collapsed = false;
39268 if(this.config.animate && skipAnim !== true){
39269 this.animateExpand();
39273 this.split.el.show();
39275 this.collapsedEl.setLocation(-2000,-2000);
39276 this.collapsedEl.hide();
39277 this.fireEvent("invalidated", this);
39278 this.fireEvent("expanded", this);
39282 animateExpand : function(){
39286 initTabs : function()
39288 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39290 var ts = new Roo.bootstrap.panel.Tabs({
39291 el: this.bodyEl.dom,
39293 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39294 disableTooltips: this.config.disableTabTips,
39295 toolbar : this.config.toolbar
39298 if(this.config.hideTabs){
39299 ts.stripWrap.setDisplayed(false);
39302 ts.resizeTabs = this.config.resizeTabs === true;
39303 ts.minTabWidth = this.config.minTabWidth || 40;
39304 ts.maxTabWidth = this.config.maxTabWidth || 250;
39305 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39306 ts.monitorResize = false;
39307 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39308 ts.bodyEl.addClass('roo-layout-tabs-body');
39309 this.panels.each(this.initPanelAsTab, this);
39312 initPanelAsTab : function(panel){
39313 var ti = this.tabs.addTab(
39317 this.config.closeOnTab && panel.isClosable(),
39320 if(panel.tabTip !== undefined){
39321 ti.setTooltip(panel.tabTip);
39323 ti.on("activate", function(){
39324 this.setActivePanel(panel);
39327 if(this.config.closeOnTab){
39328 ti.on("beforeclose", function(t, e){
39330 this.remove(panel);
39334 panel.tabItem = ti;
39339 updatePanelTitle : function(panel, title)
39341 if(this.activePanel == panel){
39342 this.updateTitle(title);
39345 var ti = this.tabs.getTab(panel.getEl().id);
39347 if(panel.tabTip !== undefined){
39348 ti.setTooltip(panel.tabTip);
39353 updateTitle : function(title){
39354 if(this.titleTextEl && !this.config.title){
39355 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39359 setActivePanel : function(panel)
39361 panel = this.getPanel(panel);
39362 if(this.activePanel && this.activePanel != panel){
39363 if(this.activePanel.setActiveState(false) === false){
39367 this.activePanel = panel;
39368 panel.setActiveState(true);
39369 if(this.panelSize){
39370 panel.setSize(this.panelSize.width, this.panelSize.height);
39373 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39375 this.updateTitle(panel.getTitle());
39377 this.fireEvent("invalidated", this);
39379 this.fireEvent("panelactivated", this, panel);
39383 * Shows the specified panel.
39384 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39385 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39387 showPanel : function(panel)
39389 panel = this.getPanel(panel);
39392 var tab = this.tabs.getTab(panel.getEl().id);
39393 if(tab.isHidden()){
39394 this.tabs.unhideTab(tab.id);
39398 this.setActivePanel(panel);
39405 * Get the active panel for this region.
39406 * @return {Roo.ContentPanel} The active panel or null
39408 getActivePanel : function(){
39409 return this.activePanel;
39412 validateVisibility : function(){
39413 if(this.panels.getCount() < 1){
39414 this.updateTitle(" ");
39415 this.closeBtn.hide();
39418 if(!this.isVisible()){
39425 * Adds the passed ContentPanel(s) to this region.
39426 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39427 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39429 add : function(panel)
39431 if(arguments.length > 1){
39432 for(var i = 0, len = arguments.length; i < len; i++) {
39433 this.add(arguments[i]);
39438 // if we have not been rendered yet, then we can not really do much of this..
39439 if (!this.bodyEl) {
39440 this.unrendered_panels.push(panel);
39447 if(this.hasPanel(panel)){
39448 this.showPanel(panel);
39451 panel.setRegion(this);
39452 this.panels.add(panel);
39453 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39454 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39455 // and hide them... ???
39456 this.bodyEl.dom.appendChild(panel.getEl().dom);
39457 if(panel.background !== true){
39458 this.setActivePanel(panel);
39460 this.fireEvent("paneladded", this, panel);
39467 this.initPanelAsTab(panel);
39471 if(panel.background !== true){
39472 this.tabs.activate(panel.getEl().id);
39474 this.fireEvent("paneladded", this, panel);
39479 * Hides the tab for the specified panel.
39480 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39482 hidePanel : function(panel){
39483 if(this.tabs && (panel = this.getPanel(panel))){
39484 this.tabs.hideTab(panel.getEl().id);
39489 * Unhides the tab for a previously hidden panel.
39490 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39492 unhidePanel : function(panel){
39493 if(this.tabs && (panel = this.getPanel(panel))){
39494 this.tabs.unhideTab(panel.getEl().id);
39498 clearPanels : function(){
39499 while(this.panels.getCount() > 0){
39500 this.remove(this.panels.first());
39505 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39506 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39507 * @param {Boolean} preservePanel Overrides the config preservePanel option
39508 * @return {Roo.ContentPanel} The panel that was removed
39510 remove : function(panel, preservePanel)
39512 panel = this.getPanel(panel);
39517 this.fireEvent("beforeremove", this, panel, e);
39518 if(e.cancel === true){
39521 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39522 var panelId = panel.getId();
39523 this.panels.removeKey(panelId);
39525 document.body.appendChild(panel.getEl().dom);
39528 this.tabs.removeTab(panel.getEl().id);
39529 }else if (!preservePanel){
39530 this.bodyEl.dom.removeChild(panel.getEl().dom);
39532 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39533 var p = this.panels.first();
39534 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39535 tempEl.appendChild(p.getEl().dom);
39536 this.bodyEl.update("");
39537 this.bodyEl.dom.appendChild(p.getEl().dom);
39539 this.updateTitle(p.getTitle());
39541 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39542 this.setActivePanel(p);
39544 panel.setRegion(null);
39545 if(this.activePanel == panel){
39546 this.activePanel = null;
39548 if(this.config.autoDestroy !== false && preservePanel !== true){
39549 try{panel.destroy();}catch(e){}
39551 this.fireEvent("panelremoved", this, panel);
39556 * Returns the TabPanel component used by this region
39557 * @return {Roo.TabPanel}
39559 getTabs : function(){
39563 createTool : function(parentEl, className){
39564 var btn = Roo.DomHelper.append(parentEl, {
39566 cls: "x-layout-tools-button",
39569 cls: "roo-layout-tools-button-inner " + className,
39573 btn.addClassOnOver("roo-layout-tools-button-over");
39578 * Ext JS Library 1.1.1
39579 * Copyright(c) 2006-2007, Ext JS, LLC.
39581 * Originally Released Under LGPL - original licence link has changed is not relivant.
39584 * <script type="text/javascript">
39590 * @class Roo.SplitLayoutRegion
39591 * @extends Roo.LayoutRegion
39592 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39594 Roo.bootstrap.layout.Split = function(config){
39595 this.cursor = config.cursor;
39596 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39599 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39601 splitTip : "Drag to resize.",
39602 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39603 useSplitTips : false,
39605 applyConfig : function(config){
39606 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39609 onRender : function(ctr,pos) {
39611 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39612 if(!this.config.split){
39617 var splitEl = Roo.DomHelper.append(ctr.dom, {
39619 id: this.el.id + "-split",
39620 cls: "roo-layout-split roo-layout-split-"+this.position,
39623 /** The SplitBar for this region
39624 * @type Roo.SplitBar */
39625 // does not exist yet...
39626 Roo.log([this.position, this.orientation]);
39628 this.split = new Roo.bootstrap.SplitBar({
39629 dragElement : splitEl,
39630 resizingElement: this.el,
39631 orientation : this.orientation
39634 this.split.on("moved", this.onSplitMove, this);
39635 this.split.useShim = this.config.useShim === true;
39636 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39637 if(this.useSplitTips){
39638 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39640 //if(config.collapsible){
39641 // this.split.el.on("dblclick", this.collapse, this);
39644 if(typeof this.config.minSize != "undefined"){
39645 this.split.minSize = this.config.minSize;
39647 if(typeof this.config.maxSize != "undefined"){
39648 this.split.maxSize = this.config.maxSize;
39650 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39651 this.hideSplitter();
39656 getHMaxSize : function(){
39657 var cmax = this.config.maxSize || 10000;
39658 var center = this.mgr.getRegion("center");
39659 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39662 getVMaxSize : function(){
39663 var cmax = this.config.maxSize || 10000;
39664 var center = this.mgr.getRegion("center");
39665 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39668 onSplitMove : function(split, newSize){
39669 this.fireEvent("resized", this, newSize);
39673 * Returns the {@link Roo.SplitBar} for this region.
39674 * @return {Roo.SplitBar}
39676 getSplitBar : function(){
39681 this.hideSplitter();
39682 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39685 hideSplitter : function(){
39687 this.split.el.setLocation(-2000,-2000);
39688 this.split.el.hide();
39694 this.split.el.show();
39696 Roo.bootstrap.layout.Split.superclass.show.call(this);
39699 beforeSlide: function(){
39700 if(Roo.isGecko){// firefox overflow auto bug workaround
39701 this.bodyEl.clip();
39703 this.tabs.bodyEl.clip();
39705 if(this.activePanel){
39706 this.activePanel.getEl().clip();
39708 if(this.activePanel.beforeSlide){
39709 this.activePanel.beforeSlide();
39715 afterSlide : function(){
39716 if(Roo.isGecko){// firefox overflow auto bug workaround
39717 this.bodyEl.unclip();
39719 this.tabs.bodyEl.unclip();
39721 if(this.activePanel){
39722 this.activePanel.getEl().unclip();
39723 if(this.activePanel.afterSlide){
39724 this.activePanel.afterSlide();
39730 initAutoHide : function(){
39731 if(this.autoHide !== false){
39732 if(!this.autoHideHd){
39733 var st = new Roo.util.DelayedTask(this.slideIn, this);
39734 this.autoHideHd = {
39735 "mouseout": function(e){
39736 if(!e.within(this.el, true)){
39740 "mouseover" : function(e){
39746 this.el.on(this.autoHideHd);
39750 clearAutoHide : function(){
39751 if(this.autoHide !== false){
39752 this.el.un("mouseout", this.autoHideHd.mouseout);
39753 this.el.un("mouseover", this.autoHideHd.mouseover);
39757 clearMonitor : function(){
39758 Roo.get(document).un("click", this.slideInIf, this);
39761 // these names are backwards but not changed for compat
39762 slideOut : function(){
39763 if(this.isSlid || this.el.hasActiveFx()){
39766 this.isSlid = true;
39767 if(this.collapseBtn){
39768 this.collapseBtn.hide();
39770 this.closeBtnState = this.closeBtn.getStyle('display');
39771 this.closeBtn.hide();
39773 this.stickBtn.show();
39776 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39777 this.beforeSlide();
39778 this.el.setStyle("z-index", 10001);
39779 this.el.slideIn(this.getSlideAnchor(), {
39780 callback: function(){
39782 this.initAutoHide();
39783 Roo.get(document).on("click", this.slideInIf, this);
39784 this.fireEvent("slideshow", this);
39791 afterSlideIn : function(){
39792 this.clearAutoHide();
39793 this.isSlid = false;
39794 this.clearMonitor();
39795 this.el.setStyle("z-index", "");
39796 if(this.collapseBtn){
39797 this.collapseBtn.show();
39799 this.closeBtn.setStyle('display', this.closeBtnState);
39801 this.stickBtn.hide();
39803 this.fireEvent("slidehide", this);
39806 slideIn : function(cb){
39807 if(!this.isSlid || this.el.hasActiveFx()){
39811 this.isSlid = false;
39812 this.beforeSlide();
39813 this.el.slideOut(this.getSlideAnchor(), {
39814 callback: function(){
39815 this.el.setLeftTop(-10000, -10000);
39817 this.afterSlideIn();
39825 slideInIf : function(e){
39826 if(!e.within(this.el)){
39831 animateCollapse : function(){
39832 this.beforeSlide();
39833 this.el.setStyle("z-index", 20000);
39834 var anchor = this.getSlideAnchor();
39835 this.el.slideOut(anchor, {
39836 callback : function(){
39837 this.el.setStyle("z-index", "");
39838 this.collapsedEl.slideIn(anchor, {duration:.3});
39840 this.el.setLocation(-10000,-10000);
39842 this.fireEvent("collapsed", this);
39849 animateExpand : function(){
39850 this.beforeSlide();
39851 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39852 this.el.setStyle("z-index", 20000);
39853 this.collapsedEl.hide({
39856 this.el.slideIn(this.getSlideAnchor(), {
39857 callback : function(){
39858 this.el.setStyle("z-index", "");
39861 this.split.el.show();
39863 this.fireEvent("invalidated", this);
39864 this.fireEvent("expanded", this);
39892 getAnchor : function(){
39893 return this.anchors[this.position];
39896 getCollapseAnchor : function(){
39897 return this.canchors[this.position];
39900 getSlideAnchor : function(){
39901 return this.sanchors[this.position];
39904 getAlignAdj : function(){
39905 var cm = this.cmargins;
39906 switch(this.position){
39922 getExpandAdj : function(){
39923 var c = this.collapsedEl, cm = this.cmargins;
39924 switch(this.position){
39926 return [-(cm.right+c.getWidth()+cm.left), 0];
39929 return [cm.right+c.getWidth()+cm.left, 0];
39932 return [0, -(cm.top+cm.bottom+c.getHeight())];
39935 return [0, cm.top+cm.bottom+c.getHeight()];
39941 * Ext JS Library 1.1.1
39942 * Copyright(c) 2006-2007, Ext JS, LLC.
39944 * Originally Released Under LGPL - original licence link has changed is not relivant.
39947 * <script type="text/javascript">
39950 * These classes are private internal classes
39952 Roo.bootstrap.layout.Center = function(config){
39953 config.region = "center";
39954 Roo.bootstrap.layout.Region.call(this, config);
39955 this.visible = true;
39956 this.minWidth = config.minWidth || 20;
39957 this.minHeight = config.minHeight || 20;
39960 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39962 // center panel can't be hidden
39966 // center panel can't be hidden
39969 getMinWidth: function(){
39970 return this.minWidth;
39973 getMinHeight: function(){
39974 return this.minHeight;
39988 Roo.bootstrap.layout.North = function(config)
39990 config.region = 'north';
39991 config.cursor = 'n-resize';
39993 Roo.bootstrap.layout.Split.call(this, config);
39997 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39998 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39999 this.split.el.addClass("roo-layout-split-v");
40001 //var size = config.initialSize || config.height;
40002 //if(this.el && typeof size != "undefined"){
40003 // this.el.setHeight(size);
40006 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40008 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40011 onRender : function(ctr, pos)
40013 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40014 var size = this.config.initialSize || this.config.height;
40015 if(this.el && typeof size != "undefined"){
40016 this.el.setHeight(size);
40021 getBox : function(){
40022 if(this.collapsed){
40023 return this.collapsedEl.getBox();
40025 var box = this.el.getBox();
40027 box.height += this.split.el.getHeight();
40032 updateBox : function(box){
40033 if(this.split && !this.collapsed){
40034 box.height -= this.split.el.getHeight();
40035 this.split.el.setLeft(box.x);
40036 this.split.el.setTop(box.y+box.height);
40037 this.split.el.setWidth(box.width);
40039 if(this.collapsed){
40040 this.updateBody(box.width, null);
40042 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40050 Roo.bootstrap.layout.South = function(config){
40051 config.region = 'south';
40052 config.cursor = 's-resize';
40053 Roo.bootstrap.layout.Split.call(this, config);
40055 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40056 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40057 this.split.el.addClass("roo-layout-split-v");
40062 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40063 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40065 onRender : function(ctr, pos)
40067 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40068 var size = this.config.initialSize || this.config.height;
40069 if(this.el && typeof size != "undefined"){
40070 this.el.setHeight(size);
40075 getBox : function(){
40076 if(this.collapsed){
40077 return this.collapsedEl.getBox();
40079 var box = this.el.getBox();
40081 var sh = this.split.el.getHeight();
40088 updateBox : function(box){
40089 if(this.split && !this.collapsed){
40090 var sh = this.split.el.getHeight();
40093 this.split.el.setLeft(box.x);
40094 this.split.el.setTop(box.y-sh);
40095 this.split.el.setWidth(box.width);
40097 if(this.collapsed){
40098 this.updateBody(box.width, null);
40100 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40104 Roo.bootstrap.layout.East = function(config){
40105 config.region = "east";
40106 config.cursor = "e-resize";
40107 Roo.bootstrap.layout.Split.call(this, config);
40109 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40110 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40111 this.split.el.addClass("roo-layout-split-h");
40115 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40116 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40118 onRender : function(ctr, pos)
40120 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40121 var size = this.config.initialSize || this.config.width;
40122 if(this.el && typeof size != "undefined"){
40123 this.el.setWidth(size);
40128 getBox : function(){
40129 if(this.collapsed){
40130 return this.collapsedEl.getBox();
40132 var box = this.el.getBox();
40134 var sw = this.split.el.getWidth();
40141 updateBox : function(box){
40142 if(this.split && !this.collapsed){
40143 var sw = this.split.el.getWidth();
40145 this.split.el.setLeft(box.x);
40146 this.split.el.setTop(box.y);
40147 this.split.el.setHeight(box.height);
40150 if(this.collapsed){
40151 this.updateBody(null, box.height);
40153 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40157 Roo.bootstrap.layout.West = function(config){
40158 config.region = "west";
40159 config.cursor = "w-resize";
40161 Roo.bootstrap.layout.Split.call(this, config);
40163 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40164 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40165 this.split.el.addClass("roo-layout-split-h");
40169 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40170 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40172 onRender: function(ctr, pos)
40174 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40175 var size = this.config.initialSize || this.config.width;
40176 if(typeof size != "undefined"){
40177 this.el.setWidth(size);
40181 getBox : function(){
40182 if(this.collapsed){
40183 return this.collapsedEl.getBox();
40185 var box = this.el.getBox();
40186 if (box.width == 0) {
40187 box.width = this.config.width; // kludge?
40190 box.width += this.split.el.getWidth();
40195 updateBox : function(box){
40196 if(this.split && !this.collapsed){
40197 var sw = this.split.el.getWidth();
40199 this.split.el.setLeft(box.x+box.width);
40200 this.split.el.setTop(box.y);
40201 this.split.el.setHeight(box.height);
40203 if(this.collapsed){
40204 this.updateBody(null, box.height);
40206 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40208 });Roo.namespace("Roo.bootstrap.panel");/*
40210 * Ext JS Library 1.1.1
40211 * Copyright(c) 2006-2007, Ext JS, LLC.
40213 * Originally Released Under LGPL - original licence link has changed is not relivant.
40216 * <script type="text/javascript">
40219 * @class Roo.bootstrap.paenl.Content
40220 * @extends Roo.util.Observable
40222 * @children Roo.bootstrap.Component
40223 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40224 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40225 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40226 * @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
40227 * @cfg {Boolean} closable True if the panel can be closed/removed
40228 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40229 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40230 * @cfg {Toolbar} toolbar A toolbar for this panel
40231 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40232 * @cfg {String} title The title for this panel
40233 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40234 * @cfg {String} url Calls {@link #setUrl} with this value
40235 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40236 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40237 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40238 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40239 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40240 * @cfg {Boolean} badges render the badges
40241 * @cfg {String} cls extra classes to use
40242 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40245 * Create a new ContentPanel.
40246 * @param {String/Object} config A string to set only the title or a config object
40249 Roo.bootstrap.panel.Content = function( config){
40251 this.tpl = config.tpl || false;
40253 var el = config.el;
40254 var content = config.content;
40256 if(config.autoCreate){ // xtype is available if this is called from factory
40259 this.el = Roo.get(el);
40260 if(!this.el && config && config.autoCreate){
40261 if(typeof config.autoCreate == "object"){
40262 if(!config.autoCreate.id){
40263 config.autoCreate.id = config.id||el;
40265 this.el = Roo.DomHelper.append(document.body,
40266 config.autoCreate, true);
40270 cls: (config.cls || '') +
40271 (config.background ? ' bg-' + config.background : '') +
40272 " roo-layout-inactive-content",
40275 if (config.iframe) {
40279 style : 'border: 0px',
40280 src : 'about:blank'
40286 elcfg.html = config.html;
40290 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40291 if (config.iframe) {
40292 this.iframeEl = this.el.select('iframe',true).first();
40297 this.closable = false;
40298 this.loaded = false;
40299 this.active = false;
40302 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40304 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40306 this.wrapEl = this.el; //this.el.wrap();
40308 if (config.toolbar.items) {
40309 ti = config.toolbar.items ;
40310 delete config.toolbar.items ;
40314 this.toolbar.render(this.wrapEl, 'before');
40315 for(var i =0;i < ti.length;i++) {
40316 // Roo.log(['add child', items[i]]);
40317 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40319 this.toolbar.items = nitems;
40320 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40321 delete config.toolbar;
40325 // xtype created footer. - not sure if will work as we normally have to render first..
40326 if (this.footer && !this.footer.el && this.footer.xtype) {
40327 if (!this.wrapEl) {
40328 this.wrapEl = this.el.wrap();
40331 this.footer.container = this.wrapEl.createChild();
40333 this.footer = Roo.factory(this.footer, Roo);
40338 if(typeof config == "string"){
40339 this.title = config;
40341 Roo.apply(this, config);
40345 this.resizeEl = Roo.get(this.resizeEl, true);
40347 this.resizeEl = this.el;
40349 // handle view.xtype
40357 * Fires when this panel is activated.
40358 * @param {Roo.ContentPanel} this
40362 * @event deactivate
40363 * Fires when this panel is activated.
40364 * @param {Roo.ContentPanel} this
40366 "deactivate" : true,
40370 * Fires when this panel is resized if fitToFrame is true.
40371 * @param {Roo.ContentPanel} this
40372 * @param {Number} width The width after any component adjustments
40373 * @param {Number} height The height after any component adjustments
40379 * Fires when this tab is created
40380 * @param {Roo.ContentPanel} this
40386 * Fires when this content is scrolled
40387 * @param {Roo.ContentPanel} this
40388 * @param {Event} scrollEvent
40399 if(this.autoScroll && !this.iframe){
40400 this.resizeEl.setStyle("overflow", "auto");
40401 this.resizeEl.on('scroll', this.onScroll, this);
40403 // fix randome scrolling
40404 //this.el.on('scroll', function() {
40405 // Roo.log('fix random scolling');
40406 // this.scrollTo('top',0);
40409 content = content || this.content;
40411 this.setContent(content);
40413 if(config && config.url){
40414 this.setUrl(this.url, this.params, this.loadOnce);
40419 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40421 if (this.view && typeof(this.view.xtype) != 'undefined') {
40422 this.view.el = this.el.appendChild(document.createElement("div"));
40423 this.view = Roo.factory(this.view);
40424 this.view.render && this.view.render(false, '');
40428 this.fireEvent('render', this);
40431 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40441 /* Resize Element - use this to work out scroll etc. */
40444 setRegion : function(region){
40445 this.region = region;
40446 this.setActiveClass(region && !this.background);
40450 setActiveClass: function(state)
40453 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40454 this.el.setStyle('position','relative');
40456 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40457 this.el.setStyle('position', 'absolute');
40462 * Returns the toolbar for this Panel if one was configured.
40463 * @return {Roo.Toolbar}
40465 getToolbar : function(){
40466 return this.toolbar;
40469 setActiveState : function(active)
40471 this.active = active;
40472 this.setActiveClass(active);
40474 if(this.fireEvent("deactivate", this) === false){
40479 this.fireEvent("activate", this);
40483 * Updates this panel's element (not for iframe)
40484 * @param {String} content The new content
40485 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40487 setContent : function(content, loadScripts){
40492 this.el.update(content, loadScripts);
40495 ignoreResize : function(w, h){
40496 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40499 this.lastSize = {width: w, height: h};
40504 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40505 * @return {Roo.UpdateManager} The UpdateManager
40507 getUpdateManager : function(){
40511 return this.el.getUpdateManager();
40514 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40515 * Does not work with IFRAME contents
40516 * @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:
40519 url: "your-url.php",
40520 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40521 callback: yourFunction,
40522 scope: yourObject, //(optional scope)
40525 text: "Loading...",
40531 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40532 * 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.
40533 * @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}
40534 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40535 * @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.
40536 * @return {Roo.ContentPanel} this
40544 var um = this.el.getUpdateManager();
40545 um.update.apply(um, arguments);
40551 * 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.
40552 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40553 * @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)
40554 * @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)
40555 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40557 setUrl : function(url, params, loadOnce){
40559 this.iframeEl.dom.src = url;
40563 if(this.refreshDelegate){
40564 this.removeListener("activate", this.refreshDelegate);
40566 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40567 this.on("activate", this.refreshDelegate);
40568 return this.el.getUpdateManager();
40571 _handleRefresh : function(url, params, loadOnce){
40572 if(!loadOnce || !this.loaded){
40573 var updater = this.el.getUpdateManager();
40574 updater.update(url, params, this._setLoaded.createDelegate(this));
40578 _setLoaded : function(){
40579 this.loaded = true;
40583 * Returns this panel's id
40586 getId : function(){
40591 * Returns this panel's element - used by regiosn to add.
40592 * @return {Roo.Element}
40594 getEl : function(){
40595 return this.wrapEl || this.el;
40600 adjustForComponents : function(width, height)
40602 //Roo.log('adjustForComponents ');
40603 if(this.resizeEl != this.el){
40604 width -= this.el.getFrameWidth('lr');
40605 height -= this.el.getFrameWidth('tb');
40608 var te = this.toolbar.getEl();
40609 te.setWidth(width);
40610 height -= te.getHeight();
40613 var te = this.footer.getEl();
40614 te.setWidth(width);
40615 height -= te.getHeight();
40619 if(this.adjustments){
40620 width += this.adjustments[0];
40621 height += this.adjustments[1];
40623 return {"width": width, "height": height};
40626 setSize : function(width, height){
40627 if(this.fitToFrame && !this.ignoreResize(width, height)){
40628 if(this.fitContainer && this.resizeEl != this.el){
40629 this.el.setSize(width, height);
40631 var size = this.adjustForComponents(width, height);
40633 this.iframeEl.setSize(width,height);
40636 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40637 this.fireEvent('resize', this, size.width, size.height);
40644 * Returns this panel's title
40647 getTitle : function(){
40649 if (typeof(this.title) != 'object') {
40654 for (var k in this.title) {
40655 if (!this.title.hasOwnProperty(k)) {
40659 if (k.indexOf('-') >= 0) {
40660 var s = k.split('-');
40661 for (var i = 0; i<s.length; i++) {
40662 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40665 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40672 * Set this panel's title
40673 * @param {String} title
40675 setTitle : function(title){
40676 this.title = title;
40678 this.region.updatePanelTitle(this, title);
40683 * Returns true is this panel was configured to be closable
40684 * @return {Boolean}
40686 isClosable : function(){
40687 return this.closable;
40690 beforeSlide : function(){
40692 this.resizeEl.clip();
40695 afterSlide : function(){
40697 this.resizeEl.unclip();
40701 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40702 * Will fail silently if the {@link #setUrl} method has not been called.
40703 * This does not activate the panel, just updates its content.
40705 refresh : function(){
40706 if(this.refreshDelegate){
40707 this.loaded = false;
40708 this.refreshDelegate();
40713 * Destroys this panel
40715 destroy : function(){
40716 this.el.removeAllListeners();
40717 var tempEl = document.createElement("span");
40718 tempEl.appendChild(this.el.dom);
40719 tempEl.innerHTML = "";
40725 * form - if the content panel contains a form - this is a reference to it.
40726 * @type {Roo.form.Form}
40730 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40731 * This contains a reference to it.
40737 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40747 * @param {Object} cfg Xtype definition of item to add.
40751 getChildContainer: function () {
40752 return this.getEl();
40756 onScroll : function(e)
40758 this.fireEvent('scroll', this, e);
40763 var ret = new Roo.factory(cfg);
40768 if (cfg.xtype.match(/^Form$/)) {
40771 //if (this.footer) {
40772 // el = this.footer.container.insertSibling(false, 'before');
40774 el = this.el.createChild();
40777 this.form = new Roo.form.Form(cfg);
40780 if ( this.form.allItems.length) {
40781 this.form.render(el.dom);
40785 // should only have one of theses..
40786 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40787 // views.. should not be just added - used named prop 'view''
40789 cfg.el = this.el.appendChild(document.createElement("div"));
40792 var ret = new Roo.factory(cfg);
40794 ret.render && ret.render(false, ''); // render blank..
40804 * @class Roo.bootstrap.panel.Grid
40805 * @extends Roo.bootstrap.panel.Content
40807 * Create a new GridPanel.
40808 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40809 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40810 * @param {Object} config A the config object
40816 Roo.bootstrap.panel.Grid = function(config)
40820 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40821 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40823 config.el = this.wrapper;
40824 //this.el = this.wrapper;
40826 if (config.container) {
40827 // ctor'ed from a Border/panel.grid
40830 this.wrapper.setStyle("overflow", "hidden");
40831 this.wrapper.addClass('roo-grid-container');
40836 if(config.toolbar){
40837 var tool_el = this.wrapper.createChild();
40838 this.toolbar = Roo.factory(config.toolbar);
40840 if (config.toolbar.items) {
40841 ti = config.toolbar.items ;
40842 delete config.toolbar.items ;
40846 this.toolbar.render(tool_el);
40847 for(var i =0;i < ti.length;i++) {
40848 // Roo.log(['add child', items[i]]);
40849 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40851 this.toolbar.items = nitems;
40853 delete config.toolbar;
40856 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40857 config.grid.scrollBody = true;;
40858 config.grid.monitorWindowResize = false; // turn off autosizing
40859 config.grid.autoHeight = false;
40860 config.grid.autoWidth = false;
40862 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40864 if (config.background) {
40865 // render grid on panel activation (if panel background)
40866 this.on('activate', function(gp) {
40867 if (!gp.grid.rendered) {
40868 gp.grid.render(this.wrapper);
40869 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40874 this.grid.render(this.wrapper);
40875 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40878 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40879 // ??? needed ??? config.el = this.wrapper;
40884 // xtype created footer. - not sure if will work as we normally have to render first..
40885 if (this.footer && !this.footer.el && this.footer.xtype) {
40887 var ctr = this.grid.getView().getFooterPanel(true);
40888 this.footer.dataSource = this.grid.dataSource;
40889 this.footer = Roo.factory(this.footer, Roo);
40890 this.footer.render(ctr);
40900 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40901 getId : function(){
40902 return this.grid.id;
40906 * Returns the grid for this panel
40907 * @return {Roo.bootstrap.Table}
40909 getGrid : function(){
40913 setSize : function(width, height){
40914 if(!this.ignoreResize(width, height)){
40915 var grid = this.grid;
40916 var size = this.adjustForComponents(width, height);
40917 // tfoot is not a footer?
40920 var gridel = grid.getGridEl();
40921 gridel.setSize(size.width, size.height);
40923 var tbd = grid.getGridEl().select('tbody', true).first();
40924 var thd = grid.getGridEl().select('thead',true).first();
40925 var tbf= grid.getGridEl().select('tfoot', true).first();
40928 size.height -= tbf.getHeight();
40931 size.height -= thd.getHeight();
40934 tbd.setSize(size.width, size.height );
40935 // this is for the account management tab -seems to work there.
40936 var thd = grid.getGridEl().select('thead',true).first();
40938 // tbd.setSize(size.width, size.height - thd.getHeight());
40947 beforeSlide : function(){
40948 this.grid.getView().scroller.clip();
40951 afterSlide : function(){
40952 this.grid.getView().scroller.unclip();
40955 destroy : function(){
40956 this.grid.destroy();
40958 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40963 * @class Roo.bootstrap.panel.Nest
40964 * @extends Roo.bootstrap.panel.Content
40966 * Create a new Panel, that can contain a layout.Border.
40969 * @param {String/Object} config A string to set only the title or a config object
40971 Roo.bootstrap.panel.Nest = function(config)
40973 // construct with only one argument..
40974 /* FIXME - implement nicer consturctors
40975 if (layout.layout) {
40977 layout = config.layout;
40978 delete config.layout;
40980 if (layout.xtype && !layout.getEl) {
40981 // then layout needs constructing..
40982 layout = Roo.factory(layout, Roo);
40986 config.el = config.layout.getEl();
40988 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40990 config.layout.monitorWindowResize = false; // turn off autosizing
40991 this.layout = config.layout;
40992 this.layout.getEl().addClass("roo-layout-nested-layout");
40993 this.layout.parent = this;
41000 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41002 * @cfg {Roo.BorderLayout} layout The layout for this panel
41006 setSize : function(width, height){
41007 if(!this.ignoreResize(width, height)){
41008 var size = this.adjustForComponents(width, height);
41009 var el = this.layout.getEl();
41010 if (size.height < 1) {
41011 el.setWidth(size.width);
41013 el.setSize(size.width, size.height);
41015 var touch = el.dom.offsetWidth;
41016 this.layout.layout();
41017 // ie requires a double layout on the first pass
41018 if(Roo.isIE && !this.initialized){
41019 this.initialized = true;
41020 this.layout.layout();
41025 // activate all subpanels if not currently active..
41027 setActiveState : function(active){
41028 this.active = active;
41029 this.setActiveClass(active);
41032 this.fireEvent("deactivate", this);
41036 this.fireEvent("activate", this);
41037 // not sure if this should happen before or after..
41038 if (!this.layout) {
41039 return; // should not happen..
41042 for (var r in this.layout.regions) {
41043 reg = this.layout.getRegion(r);
41044 if (reg.getActivePanel()) {
41045 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41046 reg.setActivePanel(reg.getActivePanel());
41049 if (!reg.panels.length) {
41052 reg.showPanel(reg.getPanel(0));
41061 * Returns the nested BorderLayout for this panel
41062 * @return {Roo.BorderLayout}
41064 getLayout : function(){
41065 return this.layout;
41069 * Adds a xtype elements to the layout of the nested panel
41073 xtype : 'ContentPanel',
41080 xtype : 'NestedLayoutPanel',
41086 items : [ ... list of content panels or nested layout panels.. ]
41090 * @param {Object} cfg Xtype definition of item to add.
41092 addxtype : function(cfg) {
41093 return this.layout.addxtype(cfg);
41098 * Ext JS Library 1.1.1
41099 * Copyright(c) 2006-2007, Ext JS, LLC.
41101 * Originally Released Under LGPL - original licence link has changed is not relivant.
41104 * <script type="text/javascript">
41107 * @class Roo.TabPanel
41108 * @extends Roo.util.Observable
41109 * A lightweight tab container.
41113 // basic tabs 1, built from existing content
41114 var tabs = new Roo.TabPanel("tabs1");
41115 tabs.addTab("script", "View Script");
41116 tabs.addTab("markup", "View Markup");
41117 tabs.activate("script");
41119 // more advanced tabs, built from javascript
41120 var jtabs = new Roo.TabPanel("jtabs");
41121 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41123 // set up the UpdateManager
41124 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41125 var updater = tab2.getUpdateManager();
41126 updater.setDefaultUrl("ajax1.htm");
41127 tab2.on('activate', updater.refresh, updater, true);
41129 // Use setUrl for Ajax loading
41130 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41131 tab3.setUrl("ajax2.htm", null, true);
41134 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41137 jtabs.activate("jtabs-1");
41140 * Create a new TabPanel.
41141 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41142 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41144 Roo.bootstrap.panel.Tabs = function(config){
41146 * The container element for this TabPanel.
41147 * @type Roo.Element
41149 this.el = Roo.get(config.el);
41152 if(typeof config == "boolean"){
41153 this.tabPosition = config ? "bottom" : "top";
41155 Roo.apply(this, config);
41159 if(this.tabPosition == "bottom"){
41160 // if tabs are at the bottom = create the body first.
41161 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41162 this.el.addClass("roo-tabs-bottom");
41164 // next create the tabs holders
41166 if (this.tabPosition == "west"){
41168 var reg = this.region; // fake it..
41170 if (!reg.mgr.parent) {
41173 reg = reg.mgr.parent.region;
41175 Roo.log("got nest?");
41177 if (reg.mgr.getRegion('west')) {
41178 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41179 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41180 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41181 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41182 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41190 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41191 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41192 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41193 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41198 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41201 // finally - if tabs are at the top, then create the body last..
41202 if(this.tabPosition != "bottom"){
41203 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41204 * @type Roo.Element
41206 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41207 this.el.addClass("roo-tabs-top");
41211 this.bodyEl.setStyle("position", "relative");
41213 this.active = null;
41214 this.activateDelegate = this.activate.createDelegate(this);
41219 * Fires when the active tab changes
41220 * @param {Roo.TabPanel} this
41221 * @param {Roo.TabPanelItem} activePanel The new active tab
41225 * @event beforetabchange
41226 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41227 * @param {Roo.TabPanel} this
41228 * @param {Object} e Set cancel to true on this object to cancel the tab change
41229 * @param {Roo.TabPanelItem} tab The tab being changed to
41231 "beforetabchange" : true
41234 Roo.EventManager.onWindowResize(this.onResize, this);
41235 this.cpad = this.el.getPadding("lr");
41236 this.hiddenCount = 0;
41239 // toolbar on the tabbar support...
41240 if (this.toolbar) {
41241 alert("no toolbar support yet");
41242 this.toolbar = false;
41244 var tcfg = this.toolbar;
41245 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41246 this.toolbar = new Roo.Toolbar(tcfg);
41247 if (Roo.isSafari) {
41248 var tbl = tcfg.container.child('table', true);
41249 tbl.setAttribute('width', '100%');
41257 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41260 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41262 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41264 tabPosition : "top",
41266 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41268 currentTabWidth : 0,
41270 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41274 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41278 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41280 preferredTabWidth : 175,
41282 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41284 resizeTabs : false,
41286 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41288 monitorResize : true,
41290 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41292 toolbar : false, // set by caller..
41294 region : false, /// set by caller
41296 disableTooltips : true, // not used yet...
41299 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41300 * @param {String} id The id of the div to use <b>or create</b>
41301 * @param {String} text The text for the tab
41302 * @param {String} content (optional) Content to put in the TabPanelItem body
41303 * @param {Boolean} closable (optional) True to create a close icon on the tab
41304 * @return {Roo.TabPanelItem} The created TabPanelItem
41306 addTab : function(id, text, content, closable, tpl)
41308 var item = new Roo.bootstrap.panel.TabItem({
41312 closable : closable,
41315 this.addTabItem(item);
41317 item.setContent(content);
41323 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41324 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41325 * @return {Roo.TabPanelItem}
41327 getTab : function(id){
41328 return this.items[id];
41332 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41333 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41335 hideTab : function(id){
41336 var t = this.items[id];
41339 this.hiddenCount++;
41340 this.autoSizeTabs();
41345 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41346 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41348 unhideTab : function(id){
41349 var t = this.items[id];
41351 t.setHidden(false);
41352 this.hiddenCount--;
41353 this.autoSizeTabs();
41358 * Adds an existing {@link Roo.TabPanelItem}.
41359 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41361 addTabItem : function(item)
41363 this.items[item.id] = item;
41364 this.items.push(item);
41365 this.autoSizeTabs();
41366 // if(this.resizeTabs){
41367 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41368 // this.autoSizeTabs();
41370 // item.autoSize();
41375 * Removes a {@link Roo.TabPanelItem}.
41376 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41378 removeTab : function(id){
41379 var items = this.items;
41380 var tab = items[id];
41381 if(!tab) { return; }
41382 var index = items.indexOf(tab);
41383 if(this.active == tab && items.length > 1){
41384 var newTab = this.getNextAvailable(index);
41389 this.stripEl.dom.removeChild(tab.pnode.dom);
41390 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41391 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41393 items.splice(index, 1);
41394 delete this.items[tab.id];
41395 tab.fireEvent("close", tab);
41396 tab.purgeListeners();
41397 this.autoSizeTabs();
41400 getNextAvailable : function(start){
41401 var items = this.items;
41403 // look for a next tab that will slide over to
41404 // replace the one being removed
41405 while(index < items.length){
41406 var item = items[++index];
41407 if(item && !item.isHidden()){
41411 // if one isn't found select the previous tab (on the left)
41414 var item = items[--index];
41415 if(item && !item.isHidden()){
41423 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41424 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41426 disableTab : function(id){
41427 var tab = this.items[id];
41428 if(tab && this.active != tab){
41434 * Enables a {@link Roo.TabPanelItem} that is disabled.
41435 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41437 enableTab : function(id){
41438 var tab = this.items[id];
41443 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41444 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41445 * @return {Roo.TabPanelItem} The TabPanelItem.
41447 activate : function(id)
41449 //Roo.log('activite:' + id);
41451 var tab = this.items[id];
41455 if(tab == this.active || tab.disabled){
41459 this.fireEvent("beforetabchange", this, e, tab);
41460 if(e.cancel !== true && !tab.disabled){
41462 this.active.hide();
41464 this.active = this.items[id];
41465 this.active.show();
41466 this.fireEvent("tabchange", this, this.active);
41472 * Gets the active {@link Roo.TabPanelItem}.
41473 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41475 getActiveTab : function(){
41476 return this.active;
41480 * Updates the tab body element to fit the height of the container element
41481 * for overflow scrolling
41482 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41484 syncHeight : function(targetHeight){
41485 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41486 var bm = this.bodyEl.getMargins();
41487 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41488 this.bodyEl.setHeight(newHeight);
41492 onResize : function(){
41493 if(this.monitorResize){
41494 this.autoSizeTabs();
41499 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41501 beginUpdate : function(){
41502 this.updating = true;
41506 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41508 endUpdate : function(){
41509 this.updating = false;
41510 this.autoSizeTabs();
41514 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41516 autoSizeTabs : function()
41518 var count = this.items.length;
41519 var vcount = count - this.hiddenCount;
41522 this.stripEl.hide();
41524 this.stripEl.show();
41527 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41532 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41533 var availWidth = Math.floor(w / vcount);
41534 var b = this.stripBody;
41535 if(b.getWidth() > w){
41536 var tabs = this.items;
41537 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41538 if(availWidth < this.minTabWidth){
41539 /*if(!this.sleft){ // incomplete scrolling code
41540 this.createScrollButtons();
41543 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41546 if(this.currentTabWidth < this.preferredTabWidth){
41547 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41553 * Returns the number of tabs in this TabPanel.
41556 getCount : function(){
41557 return this.items.length;
41561 * Resizes all the tabs to the passed width
41562 * @param {Number} The new width
41564 setTabWidth : function(width){
41565 this.currentTabWidth = width;
41566 for(var i = 0, len = this.items.length; i < len; i++) {
41567 if(!this.items[i].isHidden()) {
41568 this.items[i].setWidth(width);
41574 * Destroys this TabPanel
41575 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41577 destroy : function(removeEl){
41578 Roo.EventManager.removeResizeListener(this.onResize, this);
41579 for(var i = 0, len = this.items.length; i < len; i++){
41580 this.items[i].purgeListeners();
41582 if(removeEl === true){
41583 this.el.update("");
41588 createStrip : function(container)
41590 var strip = document.createElement("nav");
41591 strip.className = Roo.bootstrap.version == 4 ?
41592 "navbar-light bg-light" :
41593 "navbar navbar-default"; //"x-tabs-wrap";
41594 container.appendChild(strip);
41598 createStripList : function(strip)
41600 // div wrapper for retard IE
41601 // returns the "tr" element.
41602 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41603 //'<div class="x-tabs-strip-wrap">'+
41604 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41605 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41606 return strip.firstChild; //.firstChild.firstChild.firstChild;
41608 createBody : function(container)
41610 var body = document.createElement("div");
41611 Roo.id(body, "tab-body");
41612 //Roo.fly(body).addClass("x-tabs-body");
41613 Roo.fly(body).addClass("tab-content");
41614 container.appendChild(body);
41617 createItemBody :function(bodyEl, id){
41618 var body = Roo.getDom(id);
41620 body = document.createElement("div");
41623 //Roo.fly(body).addClass("x-tabs-item-body");
41624 Roo.fly(body).addClass("tab-pane");
41625 bodyEl.insertBefore(body, bodyEl.firstChild);
41629 createStripElements : function(stripEl, text, closable, tpl)
41631 var td = document.createElement("li"); // was td..
41632 td.className = 'nav-item';
41634 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41637 stripEl.appendChild(td);
41639 td.className = "x-tabs-closable";
41640 if(!this.closeTpl){
41641 this.closeTpl = new Roo.Template(
41642 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41643 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41644 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41647 var el = this.closeTpl.overwrite(td, {"text": text});
41648 var close = el.getElementsByTagName("div")[0];
41649 var inner = el.getElementsByTagName("em")[0];
41650 return {"el": el, "close": close, "inner": inner};
41653 // not sure what this is..
41654 // if(!this.tabTpl){
41655 //this.tabTpl = new Roo.Template(
41656 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41657 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41659 // this.tabTpl = new Roo.Template(
41660 // '<a href="#">' +
41661 // '<span unselectable="on"' +
41662 // (this.disableTooltips ? '' : ' title="{text}"') +
41663 // ' >{text}</span></a>'
41669 var template = tpl || this.tabTpl || false;
41672 template = new Roo.Template(
41673 Roo.bootstrap.version == 4 ?
41675 '<a class="nav-link" href="#" unselectable="on"' +
41676 (this.disableTooltips ? '' : ' title="{text}"') +
41679 '<a class="nav-link" href="#">' +
41680 '<span unselectable="on"' +
41681 (this.disableTooltips ? '' : ' title="{text}"') +
41682 ' >{text}</span></a>'
41687 switch (typeof(template)) {
41691 template = new Roo.Template(template);
41697 var el = template.overwrite(td, {"text": text});
41699 var inner = el.getElementsByTagName("span")[0];
41701 return {"el": el, "inner": inner};
41709 * @class Roo.TabPanelItem
41710 * @extends Roo.util.Observable
41711 * Represents an individual item (tab plus body) in a TabPanel.
41712 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41713 * @param {String} id The id of this TabPanelItem
41714 * @param {String} text The text for the tab of this TabPanelItem
41715 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41717 Roo.bootstrap.panel.TabItem = function(config){
41719 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41720 * @type Roo.TabPanel
41722 this.tabPanel = config.panel;
41724 * The id for this TabPanelItem
41727 this.id = config.id;
41729 this.disabled = false;
41731 this.text = config.text;
41733 this.loaded = false;
41734 this.closable = config.closable;
41737 * The body element for this TabPanelItem.
41738 * @type Roo.Element
41740 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41741 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41742 this.bodyEl.setStyle("display", "block");
41743 this.bodyEl.setStyle("zoom", "1");
41744 //this.hideAction();
41746 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41748 this.el = Roo.get(els.el);
41749 this.inner = Roo.get(els.inner, true);
41750 this.textEl = Roo.bootstrap.version == 4 ?
41751 this.el : Roo.get(this.el.dom.firstChild, true);
41753 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41754 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41757 // this.el.on("mousedown", this.onTabMouseDown, this);
41758 this.el.on("click", this.onTabClick, this);
41760 if(config.closable){
41761 var c = Roo.get(els.close, true);
41762 c.dom.title = this.closeText;
41763 c.addClassOnOver("close-over");
41764 c.on("click", this.closeClick, this);
41770 * Fires when this tab becomes the active tab.
41771 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41772 * @param {Roo.TabPanelItem} this
41776 * @event beforeclose
41777 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41778 * @param {Roo.TabPanelItem} this
41779 * @param {Object} e Set cancel to true on this object to cancel the close.
41781 "beforeclose": true,
41784 * Fires when this tab is closed.
41785 * @param {Roo.TabPanelItem} this
41789 * @event deactivate
41790 * Fires when this tab is no longer the active tab.
41791 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41792 * @param {Roo.TabPanelItem} this
41794 "deactivate" : true
41796 this.hidden = false;
41798 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41801 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41803 purgeListeners : function(){
41804 Roo.util.Observable.prototype.purgeListeners.call(this);
41805 this.el.removeAllListeners();
41808 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41811 this.status_node.addClass("active");
41814 this.tabPanel.stripWrap.repaint();
41816 this.fireEvent("activate", this.tabPanel, this);
41820 * Returns true if this tab is the active tab.
41821 * @return {Boolean}
41823 isActive : function(){
41824 return this.tabPanel.getActiveTab() == this;
41828 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41831 this.status_node.removeClass("active");
41833 this.fireEvent("deactivate", this.tabPanel, this);
41836 hideAction : function(){
41837 this.bodyEl.hide();
41838 this.bodyEl.setStyle("position", "absolute");
41839 this.bodyEl.setLeft("-20000px");
41840 this.bodyEl.setTop("-20000px");
41843 showAction : function(){
41844 this.bodyEl.setStyle("position", "relative");
41845 this.bodyEl.setTop("");
41846 this.bodyEl.setLeft("");
41847 this.bodyEl.show();
41851 * Set the tooltip for the tab.
41852 * @param {String} tooltip The tab's tooltip
41854 setTooltip : function(text){
41855 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41856 this.textEl.dom.qtip = text;
41857 this.textEl.dom.removeAttribute('title');
41859 this.textEl.dom.title = text;
41863 onTabClick : function(e){
41864 e.preventDefault();
41865 this.tabPanel.activate(this.id);
41868 onTabMouseDown : function(e){
41869 e.preventDefault();
41870 this.tabPanel.activate(this.id);
41873 getWidth : function(){
41874 return this.inner.getWidth();
41877 setWidth : function(width){
41878 var iwidth = width - this.linode.getPadding("lr");
41879 this.inner.setWidth(iwidth);
41880 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41881 this.linode.setWidth(width);
41885 * Show or hide the tab
41886 * @param {Boolean} hidden True to hide or false to show.
41888 setHidden : function(hidden){
41889 this.hidden = hidden;
41890 this.linode.setStyle("display", hidden ? "none" : "");
41894 * Returns true if this tab is "hidden"
41895 * @return {Boolean}
41897 isHidden : function(){
41898 return this.hidden;
41902 * Returns the text for this tab
41905 getText : function(){
41909 autoSize : function(){
41910 //this.el.beginMeasure();
41911 this.textEl.setWidth(1);
41913 * #2804 [new] Tabs in Roojs
41914 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41916 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41917 //this.el.endMeasure();
41921 * Sets the text for the tab (Note: this also sets the tooltip text)
41922 * @param {String} text The tab's text and tooltip
41924 setText : function(text){
41926 this.textEl.update(text);
41927 this.setTooltip(text);
41928 //if(!this.tabPanel.resizeTabs){
41929 // this.autoSize();
41933 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41935 activate : function(){
41936 this.tabPanel.activate(this.id);
41940 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41942 disable : function(){
41943 if(this.tabPanel.active != this){
41944 this.disabled = true;
41945 this.status_node.addClass("disabled");
41950 * Enables this TabPanelItem if it was previously disabled.
41952 enable : function(){
41953 this.disabled = false;
41954 this.status_node.removeClass("disabled");
41958 * Sets the content for this TabPanelItem.
41959 * @param {String} content The content
41960 * @param {Boolean} loadScripts true to look for and load scripts
41962 setContent : function(content, loadScripts){
41963 this.bodyEl.update(content, loadScripts);
41967 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41968 * @return {Roo.UpdateManager} The UpdateManager
41970 getUpdateManager : function(){
41971 return this.bodyEl.getUpdateManager();
41975 * Set a URL to be used to load the content for this TabPanelItem.
41976 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41977 * @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)
41978 * @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)
41979 * @return {Roo.UpdateManager} The UpdateManager
41981 setUrl : function(url, params, loadOnce){
41982 if(this.refreshDelegate){
41983 this.un('activate', this.refreshDelegate);
41985 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41986 this.on("activate", this.refreshDelegate);
41987 return this.bodyEl.getUpdateManager();
41991 _handleRefresh : function(url, params, loadOnce){
41992 if(!loadOnce || !this.loaded){
41993 var updater = this.bodyEl.getUpdateManager();
41994 updater.update(url, params, this._setLoaded.createDelegate(this));
41999 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42000 * Will fail silently if the setUrl method has not been called.
42001 * This does not activate the panel, just updates its content.
42003 refresh : function(){
42004 if(this.refreshDelegate){
42005 this.loaded = false;
42006 this.refreshDelegate();
42011 _setLoaded : function(){
42012 this.loaded = true;
42016 closeClick : function(e){
42019 this.fireEvent("beforeclose", this, o);
42020 if(o.cancel !== true){
42021 this.tabPanel.removeTab(this.id);
42025 * The text displayed in the tooltip for the close icon.
42028 closeText : "Close this tab"
42031 * This script refer to:
42032 * Title: International Telephone Input
42033 * Author: Jack O'Connor
42034 * Code version: v12.1.12
42035 * Availability: https://github.com/jackocnr/intl-tel-input.git
42038 Roo.bootstrap.PhoneInputData = function() {
42041 "Afghanistan (افغانستان)",
42046 "Albania (Shqipëri)",
42051 "Algeria (الجزائر)",
42076 "Antigua and Barbuda",
42086 "Armenia (Հայաստան)",
42102 "Austria (Österreich)",
42107 "Azerbaijan (Azərbaycan)",
42117 "Bahrain (البحرين)",
42122 "Bangladesh (বাংলাদেশ)",
42132 "Belarus (Беларусь)",
42137 "Belgium (België)",
42167 "Bosnia and Herzegovina (Босна и Херцеговина)",
42182 "British Indian Ocean Territory",
42187 "British Virgin Islands",
42197 "Bulgaria (България)",
42207 "Burundi (Uburundi)",
42212 "Cambodia (កម្ពុជា)",
42217 "Cameroon (Cameroun)",
42226 ["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"]
42229 "Cape Verde (Kabu Verdi)",
42234 "Caribbean Netherlands",
42245 "Central African Republic (République centrafricaine)",
42265 "Christmas Island",
42271 "Cocos (Keeling) Islands",
42282 "Comoros (جزر القمر)",
42287 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42292 "Congo (Republic) (Congo-Brazzaville)",
42312 "Croatia (Hrvatska)",
42333 "Czech Republic (Česká republika)",
42338 "Denmark (Danmark)",
42353 "Dominican Republic (República Dominicana)",
42357 ["809", "829", "849"]
42375 "Equatorial Guinea (Guinea Ecuatorial)",
42395 "Falkland Islands (Islas Malvinas)",
42400 "Faroe Islands (Føroyar)",
42421 "French Guiana (Guyane française)",
42426 "French Polynesia (Polynésie française)",
42441 "Georgia (საქართველო)",
42446 "Germany (Deutschland)",
42466 "Greenland (Kalaallit Nunaat)",
42503 "Guinea-Bissau (Guiné Bissau)",
42528 "Hungary (Magyarország)",
42533 "Iceland (Ísland)",
42553 "Iraq (العراق)",
42569 "Israel (ישראל)",
42596 "Jordan (الأردن)",
42601 "Kazakhstan (Казахстан)",
42622 "Kuwait (الكويت)",
42627 "Kyrgyzstan (Кыргызстан)",
42637 "Latvia (Latvija)",
42642 "Lebanon (لبنان)",
42657 "Libya (ليبيا)",
42667 "Lithuania (Lietuva)",
42682 "Macedonia (FYROM) (Македонија)",
42687 "Madagascar (Madagasikara)",
42717 "Marshall Islands",
42727 "Mauritania (موريتانيا)",
42732 "Mauritius (Moris)",
42753 "Moldova (Republica Moldova)",
42763 "Mongolia (Монгол)",
42768 "Montenegro (Crna Gora)",
42778 "Morocco (المغرب)",
42784 "Mozambique (Moçambique)",
42789 "Myanmar (Burma) (မြန်မာ)",
42794 "Namibia (Namibië)",
42809 "Netherlands (Nederland)",
42814 "New Caledonia (Nouvelle-Calédonie)",
42849 "North Korea (조선 민주주의 인민 공화국)",
42854 "Northern Mariana Islands",
42870 "Pakistan (پاکستان)",
42880 "Palestine (فلسطين)",
42890 "Papua New Guinea",
42932 "Réunion (La Réunion)",
42938 "Romania (România)",
42954 "Saint Barthélemy",
42965 "Saint Kitts and Nevis",
42975 "Saint Martin (Saint-Martin (partie française))",
42981 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42986 "Saint Vincent and the Grenadines",
43001 "São Tomé and Príncipe (São Tomé e Príncipe)",
43006 "Saudi Arabia (المملكة العربية السعودية)",
43011 "Senegal (Sénégal)",
43041 "Slovakia (Slovensko)",
43046 "Slovenia (Slovenija)",
43056 "Somalia (Soomaaliya)",
43066 "South Korea (대한민국)",
43071 "South Sudan (جنوب السودان)",
43081 "Sri Lanka (ශ්රී ලංකාව)",
43086 "Sudan (السودان)",
43096 "Svalbard and Jan Mayen",
43107 "Sweden (Sverige)",
43112 "Switzerland (Schweiz)",
43117 "Syria (سوريا)",
43162 "Trinidad and Tobago",
43167 "Tunisia (تونس)",
43172 "Turkey (Türkiye)",
43182 "Turks and Caicos Islands",
43192 "U.S. Virgin Islands",
43202 "Ukraine (Україна)",
43207 "United Arab Emirates (الإمارات العربية المتحدة)",
43229 "Uzbekistan (Oʻzbekiston)",
43239 "Vatican City (Città del Vaticano)",
43250 "Vietnam (Việt Nam)",
43255 "Wallis and Futuna (Wallis-et-Futuna)",
43260 "Western Sahara (الصحراء الغربية)",
43266 "Yemen (اليمن)",
43290 * This script refer to:
43291 * Title: International Telephone Input
43292 * Author: Jack O'Connor
43293 * Code version: v12.1.12
43294 * Availability: https://github.com/jackocnr/intl-tel-input.git
43298 * @class Roo.bootstrap.PhoneInput
43299 * @extends Roo.bootstrap.TriggerField
43300 * An input with International dial-code selection
43302 * @cfg {String} defaultDialCode default '+852'
43303 * @cfg {Array} preferedCountries default []
43306 * Create a new PhoneInput.
43307 * @param {Object} config Configuration options
43310 Roo.bootstrap.PhoneInput = function(config) {
43311 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43314 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43316 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43318 listWidth: undefined,
43320 selectedClass: 'active',
43322 invalidClass : "has-warning",
43324 validClass: 'has-success',
43326 allowed: '0123456789',
43331 * @cfg {String} defaultDialCode The default dial code when initializing the input
43333 defaultDialCode: '+852',
43336 * @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
43338 preferedCountries: false,
43340 getAutoCreate : function()
43342 var data = Roo.bootstrap.PhoneInputData();
43343 var align = this.labelAlign || this.parentLabelAlign();
43346 this.allCountries = [];
43347 this.dialCodeMapping = [];
43349 for (var i = 0; i < data.length; i++) {
43351 this.allCountries[i] = {
43355 priority: c[3] || 0,
43356 areaCodes: c[4] || null
43358 this.dialCodeMapping[c[2]] = {
43361 priority: c[3] || 0,
43362 areaCodes: c[4] || null
43374 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43375 maxlength: this.max_length,
43376 cls : 'form-control tel-input',
43377 autocomplete: 'new-password'
43380 var hiddenInput = {
43383 cls: 'hidden-tel-input'
43387 hiddenInput.name = this.name;
43390 if (this.disabled) {
43391 input.disabled = true;
43394 var flag_container = {
43411 cls: this.hasFeedback ? 'has-feedback' : '',
43417 cls: 'dial-code-holder',
43424 cls: 'roo-select2-container input-group',
43431 if (this.fieldLabel.length) {
43434 tooltip: 'This field is required'
43440 cls: 'control-label',
43446 html: this.fieldLabel
43449 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43455 if(this.indicatorpos == 'right') {
43456 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43463 if(align == 'left') {
43471 if(this.labelWidth > 12){
43472 label.style = "width: " + this.labelWidth + 'px';
43474 if(this.labelWidth < 13 && this.labelmd == 0){
43475 this.labelmd = this.labelWidth;
43477 if(this.labellg > 0){
43478 label.cls += ' col-lg-' + this.labellg;
43479 input.cls += ' col-lg-' + (12 - this.labellg);
43481 if(this.labelmd > 0){
43482 label.cls += ' col-md-' + this.labelmd;
43483 container.cls += ' col-md-' + (12 - this.labelmd);
43485 if(this.labelsm > 0){
43486 label.cls += ' col-sm-' + this.labelsm;
43487 container.cls += ' col-sm-' + (12 - this.labelsm);
43489 if(this.labelxs > 0){
43490 label.cls += ' col-xs-' + this.labelxs;
43491 container.cls += ' col-xs-' + (12 - this.labelxs);
43501 var settings = this;
43503 ['xs','sm','md','lg'].map(function(size){
43504 if (settings[size]) {
43505 cfg.cls += ' col-' + size + '-' + settings[size];
43509 this.store = new Roo.data.Store({
43510 proxy : new Roo.data.MemoryProxy({}),
43511 reader : new Roo.data.JsonReader({
43522 'name' : 'dialCode',
43526 'name' : 'priority',
43530 'name' : 'areaCodes',
43537 if(!this.preferedCountries) {
43538 this.preferedCountries = [
43545 var p = this.preferedCountries.reverse();
43548 for (var i = 0; i < p.length; i++) {
43549 for (var j = 0; j < this.allCountries.length; j++) {
43550 if(this.allCountries[j].iso2 == p[i]) {
43551 var t = this.allCountries[j];
43552 this.allCountries.splice(j,1);
43553 this.allCountries.unshift(t);
43559 this.store.proxy.data = {
43561 data: this.allCountries
43567 initEvents : function()
43570 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43572 this.indicator = this.indicatorEl();
43573 this.flag = this.flagEl();
43574 this.dialCodeHolder = this.dialCodeHolderEl();
43576 this.trigger = this.el.select('div.flag-box',true).first();
43577 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43582 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43583 _this.list.setWidth(lw);
43586 this.list.on('mouseover', this.onViewOver, this);
43587 this.list.on('mousemove', this.onViewMove, this);
43588 this.inputEl().on("keyup", this.onKeyUp, this);
43589 this.inputEl().on("keypress", this.onKeyPress, this);
43591 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43593 this.view = new Roo.View(this.list, this.tpl, {
43594 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43597 this.view.on('click', this.onViewClick, this);
43598 this.setValue(this.defaultDialCode);
43601 onTriggerClick : function(e)
43603 Roo.log('trigger click');
43608 if(this.isExpanded()){
43610 this.hasFocus = false;
43612 this.store.load({});
43613 this.hasFocus = true;
43618 isExpanded : function()
43620 return this.list.isVisible();
43623 collapse : function()
43625 if(!this.isExpanded()){
43629 Roo.get(document).un('mousedown', this.collapseIf, this);
43630 Roo.get(document).un('mousewheel', this.collapseIf, this);
43631 this.fireEvent('collapse', this);
43635 expand : function()
43639 if(this.isExpanded() || !this.hasFocus){
43643 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43644 this.list.setWidth(lw);
43647 this.restrictHeight();
43649 Roo.get(document).on('mousedown', this.collapseIf, this);
43650 Roo.get(document).on('mousewheel', this.collapseIf, this);
43652 this.fireEvent('expand', this);
43655 restrictHeight : function()
43657 this.list.alignTo(this.inputEl(), this.listAlign);
43658 this.list.alignTo(this.inputEl(), this.listAlign);
43661 onViewOver : function(e, t)
43663 if(this.inKeyMode){
43666 var item = this.view.findItemFromChild(t);
43669 var index = this.view.indexOf(item);
43670 this.select(index, false);
43675 onViewClick : function(view, doFocus, el, e)
43677 var index = this.view.getSelectedIndexes()[0];
43679 var r = this.store.getAt(index);
43682 this.onSelect(r, index);
43684 if(doFocus !== false && !this.blockFocus){
43685 this.inputEl().focus();
43689 onViewMove : function(e, t)
43691 this.inKeyMode = false;
43694 select : function(index, scrollIntoView)
43696 this.selectedIndex = index;
43697 this.view.select(index);
43698 if(scrollIntoView !== false){
43699 var el = this.view.getNode(index);
43701 this.list.scrollChildIntoView(el, false);
43706 createList : function()
43708 this.list = Roo.get(document.body).createChild({
43710 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43711 style: 'display:none'
43714 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43717 collapseIf : function(e)
43719 var in_combo = e.within(this.el);
43720 var in_list = e.within(this.list);
43721 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43723 if (in_combo || in_list || is_list) {
43729 onSelect : function(record, index)
43731 if(this.fireEvent('beforeselect', this, record, index) !== false){
43733 this.setFlagClass(record.data.iso2);
43734 this.setDialCode(record.data.dialCode);
43735 this.hasFocus = false;
43737 this.fireEvent('select', this, record, index);
43741 flagEl : function()
43743 var flag = this.el.select('div.flag',true).first();
43750 dialCodeHolderEl : function()
43752 var d = this.el.select('input.dial-code-holder',true).first();
43759 setDialCode : function(v)
43761 this.dialCodeHolder.dom.value = '+'+v;
43764 setFlagClass : function(n)
43766 this.flag.dom.className = 'flag '+n;
43769 getValue : function()
43771 var v = this.inputEl().getValue();
43772 if(this.dialCodeHolder) {
43773 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43778 setValue : function(v)
43780 var d = this.getDialCode(v);
43782 //invalid dial code
43783 if(v.length == 0 || !d || d.length == 0) {
43785 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43786 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43792 this.setFlagClass(this.dialCodeMapping[d].iso2);
43793 this.setDialCode(d);
43794 this.inputEl().dom.value = v.replace('+'+d,'');
43795 this.hiddenEl().dom.value = this.getValue();
43800 getDialCode : function(v)
43804 if (v.length == 0) {
43805 return this.dialCodeHolder.dom.value;
43809 if (v.charAt(0) != "+") {
43812 var numericChars = "";
43813 for (var i = 1; i < v.length; i++) {
43814 var c = v.charAt(i);
43817 if (this.dialCodeMapping[numericChars]) {
43818 dialCode = v.substr(1, i);
43820 if (numericChars.length == 4) {
43830 this.setValue(this.defaultDialCode);
43834 hiddenEl : function()
43836 return this.el.select('input.hidden-tel-input',true).first();
43839 // after setting val
43840 onKeyUp : function(e){
43841 this.setValue(this.getValue());
43844 onKeyPress : function(e){
43845 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43852 * @class Roo.bootstrap.MoneyField
43853 * @extends Roo.bootstrap.ComboBox
43854 * Bootstrap MoneyField class
43857 * Create a new MoneyField.
43858 * @param {Object} config Configuration options
43861 Roo.bootstrap.MoneyField = function(config) {
43863 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43867 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43870 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43872 allowDecimals : true,
43874 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43876 decimalSeparator : ".",
43878 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43880 decimalPrecision : 0,
43882 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43884 allowNegative : true,
43886 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43890 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43892 minValue : Number.NEGATIVE_INFINITY,
43894 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43896 maxValue : Number.MAX_VALUE,
43898 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43900 minText : "The minimum value for this field is {0}",
43902 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43904 maxText : "The maximum value for this field is {0}",
43906 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43907 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43909 nanText : "{0} is not a valid number",
43911 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43915 * @cfg {String} defaults currency of the MoneyField
43916 * value should be in lkey
43918 defaultCurrency : false,
43920 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43922 thousandsDelimiter : false,
43924 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43933 * @cfg {Roo.data.Store} store Store to lookup currency??
43937 getAutoCreate : function()
43939 var align = this.labelAlign || this.parentLabelAlign();
43951 cls : 'form-control roo-money-amount-input',
43952 autocomplete: 'new-password'
43955 var hiddenInput = {
43959 cls: 'hidden-number-input'
43962 if(this.max_length) {
43963 input.maxlength = this.max_length;
43967 hiddenInput.name = this.name;
43970 if (this.disabled) {
43971 input.disabled = true;
43974 var clg = 12 - this.inputlg;
43975 var cmd = 12 - this.inputmd;
43976 var csm = 12 - this.inputsm;
43977 var cxs = 12 - this.inputxs;
43981 cls : 'row roo-money-field',
43985 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43989 cls: 'roo-select2-container input-group',
43993 cls : 'form-control roo-money-currency-input',
43994 autocomplete: 'new-password',
43996 name : this.currencyName
44000 cls : 'input-group-addon',
44014 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44018 cls: this.hasFeedback ? 'has-feedback' : '',
44029 if (this.fieldLabel.length) {
44032 tooltip: 'This field is required'
44038 cls: 'control-label',
44044 html: this.fieldLabel
44047 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44053 if(this.indicatorpos == 'right') {
44054 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44061 if(align == 'left') {
44069 if(this.labelWidth > 12){
44070 label.style = "width: " + this.labelWidth + 'px';
44072 if(this.labelWidth < 13 && this.labelmd == 0){
44073 this.labelmd = this.labelWidth;
44075 if(this.labellg > 0){
44076 label.cls += ' col-lg-' + this.labellg;
44077 input.cls += ' col-lg-' + (12 - this.labellg);
44079 if(this.labelmd > 0){
44080 label.cls += ' col-md-' + this.labelmd;
44081 container.cls += ' col-md-' + (12 - this.labelmd);
44083 if(this.labelsm > 0){
44084 label.cls += ' col-sm-' + this.labelsm;
44085 container.cls += ' col-sm-' + (12 - this.labelsm);
44087 if(this.labelxs > 0){
44088 label.cls += ' col-xs-' + this.labelxs;
44089 container.cls += ' col-xs-' + (12 - this.labelxs);
44100 var settings = this;
44102 ['xs','sm','md','lg'].map(function(size){
44103 if (settings[size]) {
44104 cfg.cls += ' col-' + size + '-' + settings[size];
44111 initEvents : function()
44113 this.indicator = this.indicatorEl();
44115 this.initCurrencyEvent();
44117 this.initNumberEvent();
44120 initCurrencyEvent : function()
44123 throw "can not find store for combo";
44126 this.store = Roo.factory(this.store, Roo.data);
44127 this.store.parent = this;
44131 this.triggerEl = this.el.select('.input-group-addon', true).first();
44133 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44138 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44139 _this.list.setWidth(lw);
44142 this.list.on('mouseover', this.onViewOver, this);
44143 this.list.on('mousemove', this.onViewMove, this);
44144 this.list.on('scroll', this.onViewScroll, this);
44147 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44150 this.view = new Roo.View(this.list, this.tpl, {
44151 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44154 this.view.on('click', this.onViewClick, this);
44156 this.store.on('beforeload', this.onBeforeLoad, this);
44157 this.store.on('load', this.onLoad, this);
44158 this.store.on('loadexception', this.onLoadException, this);
44160 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44161 "up" : function(e){
44162 this.inKeyMode = true;
44166 "down" : function(e){
44167 if(!this.isExpanded()){
44168 this.onTriggerClick();
44170 this.inKeyMode = true;
44175 "enter" : function(e){
44178 if(this.fireEvent("specialkey", this, e)){
44179 this.onViewClick(false);
44185 "esc" : function(e){
44189 "tab" : function(e){
44192 if(this.fireEvent("specialkey", this, e)){
44193 this.onViewClick(false);
44201 doRelay : function(foo, bar, hname){
44202 if(hname == 'down' || this.scope.isExpanded()){
44203 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44211 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44215 initNumberEvent : function(e)
44217 this.inputEl().on("keydown" , this.fireKey, this);
44218 this.inputEl().on("focus", this.onFocus, this);
44219 this.inputEl().on("blur", this.onBlur, this);
44221 this.inputEl().relayEvent('keyup', this);
44223 if(this.indicator){
44224 this.indicator.addClass('invisible');
44227 this.originalValue = this.getValue();
44229 if(this.validationEvent == 'keyup'){
44230 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44231 this.inputEl().on('keyup', this.filterValidation, this);
44233 else if(this.validationEvent !== false){
44234 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44237 if(this.selectOnFocus){
44238 this.on("focus", this.preFocus, this);
44241 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44242 this.inputEl().on("keypress", this.filterKeys, this);
44244 this.inputEl().relayEvent('keypress', this);
44247 var allowed = "0123456789";
44249 if(this.allowDecimals){
44250 allowed += this.decimalSeparator;
44253 if(this.allowNegative){
44257 if(this.thousandsDelimiter) {
44261 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44263 var keyPress = function(e){
44265 var k = e.getKey();
44267 var c = e.getCharCode();
44270 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44271 allowed.indexOf(String.fromCharCode(c)) === -1
44277 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44281 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44286 this.inputEl().on("keypress", keyPress, this);
44290 onTriggerClick : function(e)
44297 this.loadNext = false;
44299 if(this.isExpanded()){
44304 this.hasFocus = true;
44306 if(this.triggerAction == 'all') {
44307 this.doQuery(this.allQuery, true);
44311 this.doQuery(this.getRawValue());
44314 getCurrency : function()
44316 var v = this.currencyEl().getValue();
44321 restrictHeight : function()
44323 this.list.alignTo(this.currencyEl(), this.listAlign);
44324 this.list.alignTo(this.currencyEl(), this.listAlign);
44327 onViewClick : function(view, doFocus, el, e)
44329 var index = this.view.getSelectedIndexes()[0];
44331 var r = this.store.getAt(index);
44334 this.onSelect(r, index);
44338 onSelect : function(record, index){
44340 if(this.fireEvent('beforeselect', this, record, index) !== false){
44342 this.setFromCurrencyData(index > -1 ? record.data : false);
44346 this.fireEvent('select', this, record, index);
44350 setFromCurrencyData : function(o)
44354 this.lastCurrency = o;
44356 if (this.currencyField) {
44357 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44359 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44362 this.lastSelectionText = currency;
44364 //setting default currency
44365 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44366 this.setCurrency(this.defaultCurrency);
44370 this.setCurrency(currency);
44373 setFromData : function(o)
44377 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44379 this.setFromCurrencyData(c);
44384 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44386 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44389 this.setValue(value);
44393 setCurrency : function(v)
44395 this.currencyValue = v;
44398 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44403 setValue : function(v)
44405 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44411 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44413 this.inputEl().dom.value = (v == '') ? '' :
44414 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44416 if(!this.allowZero && v === '0') {
44417 this.hiddenEl().dom.value = '';
44418 this.inputEl().dom.value = '';
44425 getRawValue : function()
44427 var v = this.inputEl().getValue();
44432 getValue : function()
44434 return this.fixPrecision(this.parseValue(this.getRawValue()));
44437 parseValue : function(value)
44439 if(this.thousandsDelimiter) {
44441 r = new RegExp(",", "g");
44442 value = value.replace(r, "");
44445 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44446 return isNaN(value) ? '' : value;
44450 fixPrecision : function(value)
44452 if(this.thousandsDelimiter) {
44454 r = new RegExp(",", "g");
44455 value = value.replace(r, "");
44458 var nan = isNaN(value);
44460 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44461 return nan ? '' : value;
44463 return parseFloat(value).toFixed(this.decimalPrecision);
44466 decimalPrecisionFcn : function(v)
44468 return Math.floor(v);
44471 validateValue : function(value)
44473 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44477 var num = this.parseValue(value);
44480 this.markInvalid(String.format(this.nanText, value));
44484 if(num < this.minValue){
44485 this.markInvalid(String.format(this.minText, this.minValue));
44489 if(num > this.maxValue){
44490 this.markInvalid(String.format(this.maxText, this.maxValue));
44497 validate : function()
44499 if(this.disabled || this.allowBlank){
44504 var currency = this.getCurrency();
44506 if(this.validateValue(this.getRawValue()) && currency.length){
44511 this.markInvalid();
44515 getName: function()
44520 beforeBlur : function()
44526 var v = this.parseValue(this.getRawValue());
44533 onBlur : function()
44537 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44538 //this.el.removeClass(this.focusClass);
44541 this.hasFocus = false;
44543 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44547 var v = this.getValue();
44549 if(String(v) !== String(this.startValue)){
44550 this.fireEvent('change', this, v, this.startValue);
44553 this.fireEvent("blur", this);
44556 inputEl : function()
44558 return this.el.select('.roo-money-amount-input', true).first();
44561 currencyEl : function()
44563 return this.el.select('.roo-money-currency-input', true).first();
44566 hiddenEl : function()
44568 return this.el.select('input.hidden-number-input',true).first();
44572 * @class Roo.bootstrap.BezierSignature
44573 * @extends Roo.bootstrap.Component
44574 * Bootstrap BezierSignature class
44575 * This script refer to:
44576 * Title: Signature Pad
44578 * Availability: https://github.com/szimek/signature_pad
44581 * Create a new BezierSignature
44582 * @param {Object} config The config object
44585 Roo.bootstrap.BezierSignature = function(config){
44586 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44592 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44599 mouse_btn_down: true,
44602 * @cfg {int} canvas height
44604 canvas_height: '200px',
44607 * @cfg {float|function} Radius of a single dot.
44612 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44617 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44622 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44627 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44632 * @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.
44634 bg_color: 'rgba(0, 0, 0, 0)',
44637 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44639 dot_color: 'black',
44642 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44644 velocity_filter_weight: 0.7,
44647 * @cfg {function} Callback when stroke begin.
44652 * @cfg {function} Callback when stroke end.
44656 getAutoCreate : function()
44658 var cls = 'roo-signature column';
44661 cls += ' ' + this.cls;
44671 for(var i = 0; i < col_sizes.length; i++) {
44672 if(this[col_sizes[i]]) {
44673 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44683 cls: 'roo-signature-body',
44687 cls: 'roo-signature-body-canvas',
44688 height: this.canvas_height,
44689 width: this.canvas_width
44696 style: 'display: none'
44704 initEvents: function()
44706 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44708 var canvas = this.canvasEl();
44710 // mouse && touch event swapping...
44711 canvas.dom.style.touchAction = 'none';
44712 canvas.dom.style.msTouchAction = 'none';
44714 this.mouse_btn_down = false;
44715 canvas.on('mousedown', this._handleMouseDown, this);
44716 canvas.on('mousemove', this._handleMouseMove, this);
44717 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44719 if (window.PointerEvent) {
44720 canvas.on('pointerdown', this._handleMouseDown, this);
44721 canvas.on('pointermove', this._handleMouseMove, this);
44722 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44725 if ('ontouchstart' in window) {
44726 canvas.on('touchstart', this._handleTouchStart, this);
44727 canvas.on('touchmove', this._handleTouchMove, this);
44728 canvas.on('touchend', this._handleTouchEnd, this);
44731 Roo.EventManager.onWindowResize(this.resize, this, true);
44733 // file input event
44734 this.fileEl().on('change', this.uploadImage, this);
44741 resize: function(){
44743 var canvas = this.canvasEl().dom;
44744 var ctx = this.canvasElCtx();
44745 var img_data = false;
44747 if(canvas.width > 0) {
44748 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44750 // setting canvas width will clean img data
44753 var style = window.getComputedStyle ?
44754 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44756 var padding_left = parseInt(style.paddingLeft) || 0;
44757 var padding_right = parseInt(style.paddingRight) || 0;
44759 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44762 ctx.putImageData(img_data, 0, 0);
44766 _handleMouseDown: function(e)
44768 if (e.browserEvent.which === 1) {
44769 this.mouse_btn_down = true;
44770 this.strokeBegin(e);
44774 _handleMouseMove: function (e)
44776 if (this.mouse_btn_down) {
44777 this.strokeMoveUpdate(e);
44781 _handleMouseUp: function (e)
44783 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44784 this.mouse_btn_down = false;
44789 _handleTouchStart: function (e) {
44791 e.preventDefault();
44792 if (e.browserEvent.targetTouches.length === 1) {
44793 // var touch = e.browserEvent.changedTouches[0];
44794 // this.strokeBegin(touch);
44796 this.strokeBegin(e); // assume e catching the correct xy...
44800 _handleTouchMove: function (e) {
44801 e.preventDefault();
44802 // var touch = event.targetTouches[0];
44803 // _this._strokeMoveUpdate(touch);
44804 this.strokeMoveUpdate(e);
44807 _handleTouchEnd: function (e) {
44808 var wasCanvasTouched = e.target === this.canvasEl().dom;
44809 if (wasCanvasTouched) {
44810 e.preventDefault();
44811 // var touch = event.changedTouches[0];
44812 // _this._strokeEnd(touch);
44817 reset: function () {
44818 this._lastPoints = [];
44819 this._lastVelocity = 0;
44820 this._lastWidth = (this.min_width + this.max_width) / 2;
44821 this.canvasElCtx().fillStyle = this.dot_color;
44824 strokeMoveUpdate: function(e)
44826 this.strokeUpdate(e);
44828 if (this.throttle) {
44829 this.throttleStroke(this.strokeUpdate, this.throttle);
44832 this.strokeUpdate(e);
44836 strokeBegin: function(e)
44838 var newPointGroup = {
44839 color: this.dot_color,
44843 if (typeof this.onBegin === 'function') {
44847 this.curve_data.push(newPointGroup);
44849 this.strokeUpdate(e);
44852 strokeUpdate: function(e)
44854 var rect = this.canvasEl().dom.getBoundingClientRect();
44855 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44856 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44857 var lastPoints = lastPointGroup.points;
44858 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44859 var isLastPointTooClose = lastPoint
44860 ? point.distanceTo(lastPoint) <= this.min_distance
44862 var color = lastPointGroup.color;
44863 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44864 var curve = this.addPoint(point);
44866 this.drawDot({color: color, point: point});
44869 this.drawCurve({color: color, curve: curve});
44879 strokeEnd: function(e)
44881 this.strokeUpdate(e);
44882 if (typeof this.onEnd === 'function') {
44887 addPoint: function (point) {
44888 var _lastPoints = this._lastPoints;
44889 _lastPoints.push(point);
44890 if (_lastPoints.length > 2) {
44891 if (_lastPoints.length === 3) {
44892 _lastPoints.unshift(_lastPoints[0]);
44894 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44895 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44896 _lastPoints.shift();
44902 calculateCurveWidths: function (startPoint, endPoint) {
44903 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44904 (1 - this.velocity_filter_weight) * this._lastVelocity;
44906 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44909 start: this._lastWidth
44912 this._lastVelocity = velocity;
44913 this._lastWidth = newWidth;
44917 drawDot: function (_a) {
44918 var color = _a.color, point = _a.point;
44919 var ctx = this.canvasElCtx();
44920 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44922 this.drawCurveSegment(point.x, point.y, width);
44924 ctx.fillStyle = color;
44928 drawCurve: function (_a) {
44929 var color = _a.color, curve = _a.curve;
44930 var ctx = this.canvasElCtx();
44931 var widthDelta = curve.endWidth - curve.startWidth;
44932 var drawSteps = Math.floor(curve.length()) * 2;
44934 ctx.fillStyle = color;
44935 for (var i = 0; i < drawSteps; i += 1) {
44936 var t = i / drawSteps;
44942 var x = uuu * curve.startPoint.x;
44943 x += 3 * uu * t * curve.control1.x;
44944 x += 3 * u * tt * curve.control2.x;
44945 x += ttt * curve.endPoint.x;
44946 var y = uuu * curve.startPoint.y;
44947 y += 3 * uu * t * curve.control1.y;
44948 y += 3 * u * tt * curve.control2.y;
44949 y += ttt * curve.endPoint.y;
44950 var width = curve.startWidth + ttt * widthDelta;
44951 this.drawCurveSegment(x, y, width);
44957 drawCurveSegment: function (x, y, width) {
44958 var ctx = this.canvasElCtx();
44960 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44961 this.is_empty = false;
44966 var ctx = this.canvasElCtx();
44967 var canvas = this.canvasEl().dom;
44968 ctx.fillStyle = this.bg_color;
44969 ctx.clearRect(0, 0, canvas.width, canvas.height);
44970 ctx.fillRect(0, 0, canvas.width, canvas.height);
44971 this.curve_data = [];
44973 this.is_empty = true;
44978 return this.el.select('input',true).first();
44981 canvasEl: function()
44983 return this.el.select('canvas',true).first();
44986 canvasElCtx: function()
44988 return this.el.select('canvas',true).first().dom.getContext('2d');
44991 getImage: function(type)
44993 if(this.is_empty) {
44998 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45001 drawFromImage: function(img_src)
45003 var img = new Image();
45005 img.onload = function(){
45006 this.canvasElCtx().drawImage(img, 0, 0);
45011 this.is_empty = false;
45014 selectImage: function()
45016 this.fileEl().dom.click();
45019 uploadImage: function(e)
45021 var reader = new FileReader();
45023 reader.onload = function(e){
45024 var img = new Image();
45025 img.onload = function(){
45027 this.canvasElCtx().drawImage(img, 0, 0);
45029 img.src = e.target.result;
45032 reader.readAsDataURL(e.target.files[0]);
45035 // Bezier Point Constructor
45036 Point: (function () {
45037 function Point(x, y, time) {
45040 this.time = time || Date.now();
45042 Point.prototype.distanceTo = function (start) {
45043 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45045 Point.prototype.equals = function (other) {
45046 return this.x === other.x && this.y === other.y && this.time === other.time;
45048 Point.prototype.velocityFrom = function (start) {
45049 return this.time !== start.time
45050 ? this.distanceTo(start) / (this.time - start.time)
45057 // Bezier Constructor
45058 Bezier: (function () {
45059 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45060 this.startPoint = startPoint;
45061 this.control2 = control2;
45062 this.control1 = control1;
45063 this.endPoint = endPoint;
45064 this.startWidth = startWidth;
45065 this.endWidth = endWidth;
45067 Bezier.fromPoints = function (points, widths, scope) {
45068 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45069 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45070 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45072 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45073 var dx1 = s1.x - s2.x;
45074 var dy1 = s1.y - s2.y;
45075 var dx2 = s2.x - s3.x;
45076 var dy2 = s2.y - s3.y;
45077 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45078 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45079 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45080 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45081 var dxm = m1.x - m2.x;
45082 var dym = m1.y - m2.y;
45083 var k = l2 / (l1 + l2);
45084 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45085 var tx = s2.x - cm.x;
45086 var ty = s2.y - cm.y;
45088 c1: new scope.Point(m1.x + tx, m1.y + ty),
45089 c2: new scope.Point(m2.x + tx, m2.y + ty)
45092 Bezier.prototype.length = function () {
45097 for (var i = 0; i <= steps; i += 1) {
45099 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45100 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45102 var xdiff = cx - px;
45103 var ydiff = cy - py;
45104 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45111 Bezier.prototype.point = function (t, start, c1, c2, end) {
45112 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45113 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45114 + (3.0 * c2 * (1.0 - t) * t * t)
45115 + (end * t * t * t);
45120 throttleStroke: function(fn, wait) {
45121 if (wait === void 0) { wait = 250; }
45123 var timeout = null;
45127 var later = function () {
45128 previous = Date.now();
45130 result = fn.apply(storedContext, storedArgs);
45132 storedContext = null;
45136 return function wrapper() {
45138 for (var _i = 0; _i < arguments.length; _i++) {
45139 args[_i] = arguments[_i];
45141 var now = Date.now();
45142 var remaining = wait - (now - previous);
45143 storedContext = this;
45145 if (remaining <= 0 || remaining > wait) {
45147 clearTimeout(timeout);
45151 result = fn.apply(storedContext, storedArgs);
45153 storedContext = null;
45157 else if (!timeout) {
45158 timeout = window.setTimeout(later, remaining);