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.nav.Bar
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 * @children Roo.bootstrap.Component
7441 * Bootstrap Breadcrumb Nav Class
7444 * @cfg {String} html the content of the link.
7445 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7446 * @cfg {Boolean} active is it active
7450 * Create a new breadcrumb.Nav
7451 * @param {Object} config The config object
7454 Roo.bootstrap.breadcrumb.Item = function(config){
7455 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7460 * The img click event for the img.
7461 * @param {Roo.EventObject} e
7468 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7473 getAutoCreate : function()
7478 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7480 if (this.href !== false) {
7487 cfg.html = this.html;
7493 initEvents: function()
7496 this.el.select('a', true).first().on('click',this.onClick, this)
7500 onClick : function(e)
7503 this.fireEvent('click',this, e);
7516 * @class Roo.bootstrap.Row
7517 * @extends Roo.bootstrap.Component
7518 * @children Roo.bootstrap.Component
7519 * Bootstrap Row class (contains columns...)
7523 * @param {Object} config The config object
7526 Roo.bootstrap.Row = function(config){
7527 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7530 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7532 getAutoCreate : function(){
7551 * @class Roo.bootstrap.Pagination
7552 * @extends Roo.bootstrap.Component
7553 * @children Roo.bootstrap.Pagination
7554 * Bootstrap Pagination class
7556 * @cfg {String} size (xs|sm|md|lg|xl)
7557 * @cfg {Boolean} inverse
7560 * Create a new Pagination
7561 * @param {Object} config The config object
7564 Roo.bootstrap.Pagination = function(config){
7565 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7568 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7574 getAutoCreate : function(){
7580 cfg.cls += ' inverse';
7586 cfg.cls += " " + this.cls;
7604 * @class Roo.bootstrap.PaginationItem
7605 * @extends Roo.bootstrap.Component
7606 * Bootstrap PaginationItem class
7607 * @cfg {String} html text
7608 * @cfg {String} href the link
7609 * @cfg {Boolean} preventDefault (true | false) default true
7610 * @cfg {Boolean} active (true | false) default false
7611 * @cfg {Boolean} disabled default false
7615 * Create a new PaginationItem
7616 * @param {Object} config The config object
7620 Roo.bootstrap.PaginationItem = function(config){
7621 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7626 * The raw click event for the entire grid.
7627 * @param {Roo.EventObject} e
7633 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7637 preventDefault: true,
7642 getAutoCreate : function(){
7648 href : this.href ? this.href : '#',
7649 html : this.html ? this.html : ''
7659 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7663 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7669 initEvents: function() {
7671 this.el.on('click', this.onClick, this);
7674 onClick : function(e)
7676 Roo.log('PaginationItem on click ');
7677 if(this.preventDefault){
7685 this.fireEvent('click', this, e);
7701 * @class Roo.bootstrap.Slider
7702 * @extends Roo.bootstrap.Component
7703 * Bootstrap Slider class
7706 * Create a new Slider
7707 * @param {Object} config The config object
7710 Roo.bootstrap.Slider = function(config){
7711 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7714 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7716 getAutoCreate : function(){
7720 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7724 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7736 * Ext JS Library 1.1.1
7737 * Copyright(c) 2006-2007, Ext JS, LLC.
7739 * Originally Released Under LGPL - original licence link has changed is not relivant.
7742 * <script type="text/javascript">
7745 * @extends Roo.dd.DDProxy
7746 * @class Roo.grid.SplitDragZone
7747 * Support for Column Header resizing
7749 * @param {Object} config
7752 // This is a support class used internally by the Grid components
7753 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7755 this.view = grid.getView();
7756 this.proxy = this.view.resizeProxy;
7757 Roo.grid.SplitDragZone.superclass.constructor.call(
7760 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7762 dragElId : Roo.id(this.proxy.dom),
7767 this.setHandleElId(Roo.id(hd));
7768 if (hd2 !== false) {
7769 this.setOuterHandleElId(Roo.id(hd2));
7772 this.scroll = false;
7774 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7775 fly: Roo.Element.fly,
7777 b4StartDrag : function(x, y){
7778 this.view.headersDisabled = true;
7779 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7780 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7782 this.proxy.setHeight(h);
7784 // for old system colWidth really stored the actual width?
7785 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7786 // which in reality did not work.. - it worked only for fixed sizes
7787 // for resizable we need to use actual sizes.
7788 var w = this.cm.getColumnWidth(this.cellIndex);
7789 if (!this.view.mainWrap) {
7791 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7796 // this was w-this.grid.minColumnWidth;
7797 // doesnt really make sense? - w = thie curren width or the rendered one?
7798 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7799 this.resetConstraints();
7800 this.setXConstraint(minw, 1000);
7801 this.setYConstraint(0, 0);
7802 this.minX = x - minw;
7803 this.maxX = x + 1000;
7805 if (!this.view.mainWrap) { // this is Bootstrap code..
7806 this.getDragEl().style.display='block';
7809 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7813 handleMouseDown : function(e){
7814 ev = Roo.EventObject.setEvent(e);
7815 var t = this.fly(ev.getTarget());
7816 if(t.hasClass("x-grid-split")){
7817 this.cellIndex = this.view.getCellIndex(t.dom);
7819 this.cm = this.grid.colModel;
7820 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7821 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7826 endDrag : function(e){
7827 this.view.headersDisabled = false;
7828 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7829 var diff = endX - this.startPos;
7831 var w = this.cm.getColumnWidth(this.cellIndex);
7832 if (!this.view.mainWrap) {
7835 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7838 autoOffset : function(){
7843 * Ext JS Library 1.1.1
7844 * Copyright(c) 2006-2007, Ext JS, LLC.
7846 * Originally Released Under LGPL - original licence link has changed is not relivant.
7849 * <script type="text/javascript">
7853 * @class Roo.grid.AbstractSelectionModel
7854 * @extends Roo.util.Observable
7856 * Abstract base class for grid SelectionModels. It provides the interface that should be
7857 * implemented by descendant classes. This class should not be directly instantiated.
7860 Roo.grid.AbstractSelectionModel = function(){
7861 this.locked = false;
7862 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7865 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7866 /** @ignore Called by the grid automatically. Do not call directly. */
7867 init : function(grid){
7873 * Locks the selections.
7880 * Unlocks the selections.
7882 unlock : function(){
7883 this.locked = false;
7887 * Returns true if the selections are locked.
7890 isLocked : function(){
7895 * Ext JS Library 1.1.1
7896 * Copyright(c) 2006-2007, Ext JS, LLC.
7898 * Originally Released Under LGPL - original licence link has changed is not relivant.
7901 * <script type="text/javascript">
7904 * @extends Roo.grid.AbstractSelectionModel
7905 * @class Roo.grid.RowSelectionModel
7906 * The default SelectionModel used by {@link Roo.grid.Grid}.
7907 * It supports multiple selections and keyboard selection/navigation.
7909 * @param {Object} config
7911 Roo.grid.RowSelectionModel = function(config){
7912 Roo.apply(this, config);
7913 this.selections = new Roo.util.MixedCollection(false, function(o){
7918 this.lastActive = false;
7922 * @event selectionchange
7923 * Fires when the selection changes
7924 * @param {SelectionModel} this
7926 "selectionchange" : true,
7928 * @event afterselectionchange
7929 * Fires after the selection changes (eg. by key press or clicking)
7930 * @param {SelectionModel} this
7932 "afterselectionchange" : true,
7934 * @event beforerowselect
7935 * Fires when a row is selected being selected, return false to cancel.
7936 * @param {SelectionModel} this
7937 * @param {Number} rowIndex The selected index
7938 * @param {Boolean} keepExisting False if other selections will be cleared
7940 "beforerowselect" : true,
7943 * Fires when a row is selected.
7944 * @param {SelectionModel} this
7945 * @param {Number} rowIndex The selected index
7946 * @param {Roo.data.Record} r The record
7950 * @event rowdeselect
7951 * Fires when a row is deselected.
7952 * @param {SelectionModel} this
7953 * @param {Number} rowIndex The selected index
7955 "rowdeselect" : true
7957 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7958 this.locked = false;
7961 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7963 * @cfg {Boolean} singleSelect
7964 * True to allow selection of only one row at a time (defaults to false)
7966 singleSelect : false,
7969 initEvents : function(){
7971 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7972 this.grid.on("mousedown", this.handleMouseDown, this);
7973 }else{ // allow click to work like normal
7974 this.grid.on("rowclick", this.handleDragableRowClick, this);
7976 // bootstrap does not have a view..
7977 var view = this.grid.view ? this.grid.view : this.grid;
7978 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7981 this.selectPrevious(e.shiftKey);
7982 }else if(this.last !== false && this.lastActive !== false){
7983 var last = this.last;
7984 this.selectRange(this.last, this.lastActive-1);
7985 view.focusRow(this.lastActive);
7990 this.selectFirstRow();
7992 this.fireEvent("afterselectionchange", this);
7994 "down" : function(e){
7996 this.selectNext(e.shiftKey);
7997 }else if(this.last !== false && this.lastActive !== false){
7998 var last = this.last;
7999 this.selectRange(this.last, this.lastActive+1);
8000 view.focusRow(this.lastActive);
8005 this.selectFirstRow();
8007 this.fireEvent("afterselectionchange", this);
8013 view.on("refresh", this.onRefresh, this);
8014 view.on("rowupdated", this.onRowUpdated, this);
8015 view.on("rowremoved", this.onRemove, this);
8019 onRefresh : function(){
8020 var ds = this.grid.ds, i, v = this.grid.view;
8021 var s = this.selections;
8023 if((i = ds.indexOfId(r.id)) != -1){
8025 s.add(ds.getAt(i)); // updating the selection relate data
8033 onRemove : function(v, index, r){
8034 this.selections.remove(r);
8038 onRowUpdated : function(v, index, r){
8039 if(this.isSelected(r)){
8040 v.onRowSelect(index);
8046 * @param {Array} records The records to select
8047 * @param {Boolean} keepExisting (optional) True to keep existing selections
8049 selectRecords : function(records, keepExisting){
8051 this.clearSelections();
8053 var ds = this.grid.ds;
8054 for(var i = 0, len = records.length; i < len; i++){
8055 this.selectRow(ds.indexOf(records[i]), true);
8060 * Gets the number of selected rows.
8063 getCount : function(){
8064 return this.selections.length;
8068 * Selects the first row in the grid.
8070 selectFirstRow : function(){
8075 * Select the last row.
8076 * @param {Boolean} keepExisting (optional) True to keep existing selections
8078 selectLastRow : function(keepExisting){
8079 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8083 * Selects the row immediately following the last selected row.
8084 * @param {Boolean} keepExisting (optional) True to keep existing selections
8086 selectNext : function(keepExisting){
8087 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8088 this.selectRow(this.last+1, keepExisting);
8089 var view = this.grid.view ? this.grid.view : this.grid;
8090 view.focusRow(this.last);
8095 * Selects the row that precedes the last selected row.
8096 * @param {Boolean} keepExisting (optional) True to keep existing selections
8098 selectPrevious : function(keepExisting){
8100 this.selectRow(this.last-1, keepExisting);
8101 var view = this.grid.view ? this.grid.view : this.grid;
8102 view.focusRow(this.last);
8107 * Returns the selected records
8108 * @return {Array} Array of selected records
8110 getSelections : function(){
8111 return [].concat(this.selections.items);
8115 * Returns the first selected record.
8118 getSelected : function(){
8119 return this.selections.itemAt(0);
8124 * Clears all selections.
8126 clearSelections : function(fast){
8131 var ds = this.grid.ds;
8132 var s = this.selections;
8134 this.deselectRow(ds.indexOfId(r.id));
8138 this.selections.clear();
8147 selectAll : function(){
8151 this.selections.clear();
8152 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8153 this.selectRow(i, true);
8158 * Returns True if there is a selection.
8161 hasSelection : function(){
8162 return this.selections.length > 0;
8166 * Returns True if the specified row is selected.
8167 * @param {Number/Record} record The record or index of the record to check
8170 isSelected : function(index){
8171 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8172 return (r && this.selections.key(r.id) ? true : false);
8176 * Returns True if the specified record id is selected.
8177 * @param {String} id The id of record to check
8180 isIdSelected : function(id){
8181 return (this.selections.key(id) ? true : false);
8185 handleMouseDown : function(e, t)
8187 var view = this.grid.view ? this.grid.view : this.grid;
8189 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8192 if(e.shiftKey && this.last !== false){
8193 var last = this.last;
8194 this.selectRange(last, rowIndex, e.ctrlKey);
8195 this.last = last; // reset the last
8196 view.focusRow(rowIndex);
8198 var isSelected = this.isSelected(rowIndex);
8199 if(e.button !== 0 && isSelected){
8200 view.focusRow(rowIndex);
8201 }else if(e.ctrlKey && isSelected){
8202 this.deselectRow(rowIndex);
8203 }else if(!isSelected){
8204 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8205 view.focusRow(rowIndex);
8208 this.fireEvent("afterselectionchange", this);
8211 handleDragableRowClick : function(grid, rowIndex, e)
8213 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8214 this.selectRow(rowIndex, false);
8215 var view = this.grid.view ? this.grid.view : this.grid;
8216 view.focusRow(rowIndex);
8217 this.fireEvent("afterselectionchange", this);
8222 * Selects multiple rows.
8223 * @param {Array} rows Array of the indexes of the row to select
8224 * @param {Boolean} keepExisting (optional) True to keep existing selections
8226 selectRows : function(rows, keepExisting){
8228 this.clearSelections();
8230 for(var i = 0, len = rows.length; i < len; i++){
8231 this.selectRow(rows[i], true);
8236 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8237 * @param {Number} startRow The index of the first row in the range
8238 * @param {Number} endRow The index of the last row in the range
8239 * @param {Boolean} keepExisting (optional) True to retain existing selections
8241 selectRange : function(startRow, endRow, keepExisting){
8246 this.clearSelections();
8248 if(startRow <= endRow){
8249 for(var i = startRow; i <= endRow; i++){
8250 this.selectRow(i, true);
8253 for(var i = startRow; i >= endRow; i--){
8254 this.selectRow(i, true);
8260 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8261 * @param {Number} startRow The index of the first row in the range
8262 * @param {Number} endRow The index of the last row in the range
8264 deselectRange : function(startRow, endRow, preventViewNotify){
8268 for(var i = startRow; i <= endRow; i++){
8269 this.deselectRow(i, preventViewNotify);
8275 * @param {Number} row The index of the row to select
8276 * @param {Boolean} keepExisting (optional) True to keep existing selections
8278 selectRow : function(index, keepExisting, preventViewNotify){
8279 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8282 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8283 if(!keepExisting || this.singleSelect){
8284 this.clearSelections();
8286 var r = this.grid.ds.getAt(index);
8287 this.selections.add(r);
8288 this.last = this.lastActive = index;
8289 if(!preventViewNotify){
8290 var view = this.grid.view ? this.grid.view : this.grid;
8291 view.onRowSelect(index);
8293 this.fireEvent("rowselect", this, index, r);
8294 this.fireEvent("selectionchange", this);
8300 * @param {Number} row The index of the row to deselect
8302 deselectRow : function(index, preventViewNotify){
8306 if(this.last == index){
8309 if(this.lastActive == index){
8310 this.lastActive = false;
8312 var r = this.grid.ds.getAt(index);
8313 this.selections.remove(r);
8314 if(!preventViewNotify){
8315 var view = this.grid.view ? this.grid.view : this.grid;
8316 view.onRowDeselect(index);
8318 this.fireEvent("rowdeselect", this, index);
8319 this.fireEvent("selectionchange", this);
8323 restoreLast : function(){
8325 this.last = this._last;
8330 acceptsNav : function(row, col, cm){
8331 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8335 onEditorKey : function(field, e){
8336 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8341 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8343 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8345 }else if(k == e.ENTER && !e.ctrlKey){
8349 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8351 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8353 }else if(k == e.ESC){
8357 g.startEditing(newCell[0], newCell[1]);
8362 * Ext JS Library 1.1.1
8363 * Copyright(c) 2006-2007, Ext JS, LLC.
8365 * Originally Released Under LGPL - original licence link has changed is not relivant.
8368 * <script type="text/javascript">
8373 * @class Roo.grid.ColumnModel
8374 * @extends Roo.util.Observable
8375 * This is the default implementation of a ColumnModel used by the Grid. It defines
8376 * the columns in the grid.
8379 var colModel = new Roo.grid.ColumnModel([
8380 {header: "Ticker", width: 60, sortable: true, locked: true},
8381 {header: "Company Name", width: 150, sortable: true},
8382 {header: "Market Cap.", width: 100, sortable: true},
8383 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8384 {header: "Employees", width: 100, sortable: true, resizable: false}
8389 * The config options listed for this class are options which may appear in each
8390 * individual column definition.
8391 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8393 * @param {Object} config An Array of column config objects. See this class's
8394 * config objects for details.
8396 Roo.grid.ColumnModel = function(config){
8398 * The config passed into the constructor
8400 this.config = []; //config;
8403 // if no id, create one
8404 // if the column does not have a dataIndex mapping,
8405 // map it to the order it is in the config
8406 for(var i = 0, len = config.length; i < len; i++){
8407 this.addColumn(config[i]);
8412 * The width of columns which have no width specified (defaults to 100)
8415 this.defaultWidth = 100;
8418 * Default sortable of columns which have no sortable specified (defaults to false)
8421 this.defaultSortable = false;
8425 * @event widthchange
8426 * Fires when the width of a column changes.
8427 * @param {ColumnModel} this
8428 * @param {Number} columnIndex The column index
8429 * @param {Number} newWidth The new width
8431 "widthchange": true,
8433 * @event headerchange
8434 * Fires when the text of a header changes.
8435 * @param {ColumnModel} this
8436 * @param {Number} columnIndex The column index
8437 * @param {Number} newText The new header text
8439 "headerchange": true,
8441 * @event hiddenchange
8442 * Fires when a column is hidden or "unhidden".
8443 * @param {ColumnModel} this
8444 * @param {Number} columnIndex The column index
8445 * @param {Boolean} hidden true if hidden, false otherwise
8447 "hiddenchange": true,
8449 * @event columnmoved
8450 * Fires when a column is moved.
8451 * @param {ColumnModel} this
8452 * @param {Number} oldIndex
8453 * @param {Number} newIndex
8455 "columnmoved" : true,
8457 * @event columlockchange
8458 * Fires when a column's locked state is changed
8459 * @param {ColumnModel} this
8460 * @param {Number} colIndex
8461 * @param {Boolean} locked true if locked
8463 "columnlockchange" : true
8465 Roo.grid.ColumnModel.superclass.constructor.call(this);
8467 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8469 * @cfg {String} header The header text to display in the Grid view.
8472 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8475 * @cfg {String} smHeader Header at Bootsrap Small width
8478 * @cfg {String} mdHeader Header at Bootsrap Medium width
8481 * @cfg {String} lgHeader Header at Bootsrap Large width
8484 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8487 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8488 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8489 * specified, the column's index is used as an index into the Record's data Array.
8492 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8493 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8496 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8497 * Defaults to the value of the {@link #defaultSortable} property.
8498 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8501 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8504 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8507 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8510 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8513 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8514 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8515 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8516 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8519 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8522 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8525 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8528 * @cfg {String} cursor (Optional)
8531 * @cfg {String} tooltip (Optional)
8534 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8537 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8540 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8543 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8546 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8549 * Returns the id of the column at the specified index.
8550 * @param {Number} index The column index
8551 * @return {String} the id
8553 getColumnId : function(index){
8554 return this.config[index].id;
8558 * Returns the column for a specified id.
8559 * @param {String} id The column id
8560 * @return {Object} the column
8562 getColumnById : function(id){
8563 return this.lookup[id];
8568 * Returns the column Object for a specified dataIndex.
8569 * @param {String} dataIndex The column dataIndex
8570 * @return {Object|Boolean} the column or false if not found
8572 getColumnByDataIndex: function(dataIndex){
8573 var index = this.findColumnIndex(dataIndex);
8574 return index > -1 ? this.config[index] : false;
8578 * Returns the index for a specified column id.
8579 * @param {String} id The column id
8580 * @return {Number} the index, or -1 if not found
8582 getIndexById : function(id){
8583 for(var i = 0, len = this.config.length; i < len; i++){
8584 if(this.config[i].id == id){
8592 * Returns the index for a specified column dataIndex.
8593 * @param {String} dataIndex The column dataIndex
8594 * @return {Number} the index, or -1 if not found
8597 findColumnIndex : function(dataIndex){
8598 for(var i = 0, len = this.config.length; i < len; i++){
8599 if(this.config[i].dataIndex == dataIndex){
8607 moveColumn : function(oldIndex, newIndex){
8608 var c = this.config[oldIndex];
8609 this.config.splice(oldIndex, 1);
8610 this.config.splice(newIndex, 0, c);
8611 this.dataMap = null;
8612 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8615 isLocked : function(colIndex){
8616 return this.config[colIndex].locked === true;
8619 setLocked : function(colIndex, value, suppressEvent){
8620 if(this.isLocked(colIndex) == value){
8623 this.config[colIndex].locked = value;
8625 this.fireEvent("columnlockchange", this, colIndex, value);
8629 getTotalLockedWidth : function(){
8631 for(var i = 0; i < this.config.length; i++){
8632 if(this.isLocked(i) && !this.isHidden(i)){
8633 this.totalWidth += this.getColumnWidth(i);
8639 getLockedCount : function(){
8640 for(var i = 0, len = this.config.length; i < len; i++){
8641 if(!this.isLocked(i)){
8646 return this.config.length;
8650 * Returns the number of columns.
8653 getColumnCount : function(visibleOnly){
8654 if(visibleOnly === true){
8656 for(var i = 0, len = this.config.length; i < len; i++){
8657 if(!this.isHidden(i)){
8663 return this.config.length;
8667 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8668 * @param {Function} fn
8669 * @param {Object} scope (optional)
8670 * @return {Array} result
8672 getColumnsBy : function(fn, scope){
8674 for(var i = 0, len = this.config.length; i < len; i++){
8675 var c = this.config[i];
8676 if(fn.call(scope||this, c, i) === true){
8684 * Returns true if the specified column is sortable.
8685 * @param {Number} col The column index
8688 isSortable : function(col){
8689 if(typeof this.config[col].sortable == "undefined"){
8690 return this.defaultSortable;
8692 return this.config[col].sortable;
8696 * Returns the rendering (formatting) function defined for the column.
8697 * @param {Number} col The column index.
8698 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8700 getRenderer : function(col){
8701 if(!this.config[col].renderer){
8702 return Roo.grid.ColumnModel.defaultRenderer;
8704 return this.config[col].renderer;
8708 * Sets the rendering (formatting) function for a column.
8709 * @param {Number} col The column index
8710 * @param {Function} fn The function to use to process the cell's raw data
8711 * to return HTML markup for the grid view. The render function is called with
8712 * the following parameters:<ul>
8713 * <li>Data value.</li>
8714 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8715 * <li>css A CSS style string to apply to the table cell.</li>
8716 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8717 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8718 * <li>Row index</li>
8719 * <li>Column index</li>
8720 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8722 setRenderer : function(col, fn){
8723 this.config[col].renderer = fn;
8727 * Returns the width for the specified column.
8728 * @param {Number} col The column index
8729 * @param (optional) {String} gridSize bootstrap width size.
8732 getColumnWidth : function(col, gridSize)
8734 var cfg = this.config[col];
8736 if (typeof(gridSize) == 'undefined') {
8737 return cfg.width * 1 || this.defaultWidth;
8739 if (gridSize === false) { // if we set it..
8740 return cfg.width || false;
8742 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8744 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8745 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8748 return cfg[ sizes[i] ];
8755 * Sets the width for a column.
8756 * @param {Number} col The column index
8757 * @param {Number} width The new width
8759 setColumnWidth : function(col, width, suppressEvent){
8760 this.config[col].width = width;
8761 this.totalWidth = null;
8763 this.fireEvent("widthchange", this, col, width);
8768 * Returns the total width of all columns.
8769 * @param {Boolean} includeHidden True to include hidden column widths
8772 getTotalWidth : function(includeHidden){
8773 if(!this.totalWidth){
8774 this.totalWidth = 0;
8775 for(var i = 0, len = this.config.length; i < len; i++){
8776 if(includeHidden || !this.isHidden(i)){
8777 this.totalWidth += this.getColumnWidth(i);
8781 return this.totalWidth;
8785 * Returns the header for the specified column.
8786 * @param {Number} col The column index
8789 getColumnHeader : function(col){
8790 return this.config[col].header;
8794 * Sets the header for a column.
8795 * @param {Number} col The column index
8796 * @param {String} header The new header
8798 setColumnHeader : function(col, header){
8799 this.config[col].header = header;
8800 this.fireEvent("headerchange", this, col, header);
8804 * Returns the tooltip for the specified column.
8805 * @param {Number} col The column index
8808 getColumnTooltip : function(col){
8809 return this.config[col].tooltip;
8812 * Sets the tooltip for a column.
8813 * @param {Number} col The column index
8814 * @param {String} tooltip The new tooltip
8816 setColumnTooltip : function(col, tooltip){
8817 this.config[col].tooltip = tooltip;
8821 * Returns the dataIndex for the specified column.
8822 * @param {Number} col The column index
8825 getDataIndex : function(col){
8826 return this.config[col].dataIndex;
8830 * Sets the dataIndex for a column.
8831 * @param {Number} col The column index
8832 * @param {Number} dataIndex The new dataIndex
8834 setDataIndex : function(col, dataIndex){
8835 this.config[col].dataIndex = dataIndex;
8841 * Returns true if the cell is editable.
8842 * @param {Number} colIndex The column index
8843 * @param {Number} rowIndex The row index - this is nto actually used..?
8846 isCellEditable : function(colIndex, rowIndex){
8847 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8851 * Returns the editor defined for the cell/column.
8852 * return false or null to disable editing.
8853 * @param {Number} colIndex The column index
8854 * @param {Number} rowIndex The row index
8857 getCellEditor : function(colIndex, rowIndex){
8858 return this.config[colIndex].editor;
8862 * Sets if a column is editable.
8863 * @param {Number} col The column index
8864 * @param {Boolean} editable True if the column is editable
8866 setEditable : function(col, editable){
8867 this.config[col].editable = editable;
8872 * Returns true if the column is hidden.
8873 * @param {Number} colIndex The column index
8876 isHidden : function(colIndex){
8877 return this.config[colIndex].hidden;
8882 * Returns true if the column width cannot be changed
8884 isFixed : function(colIndex){
8885 return this.config[colIndex].fixed;
8889 * Returns true if the column can be resized
8892 isResizable : function(colIndex){
8893 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8896 * Sets if a column is hidden.
8897 * @param {Number} colIndex The column index
8898 * @param {Boolean} hidden True if the column is hidden
8900 setHidden : function(colIndex, hidden){
8901 this.config[colIndex].hidden = hidden;
8902 this.totalWidth = null;
8903 this.fireEvent("hiddenchange", this, colIndex, hidden);
8907 * Sets the editor for a column.
8908 * @param {Number} col The column index
8909 * @param {Object} editor The editor object
8911 setEditor : function(col, editor){
8912 this.config[col].editor = editor;
8915 * Add a column (experimental...) - defaults to adding to the end..
8916 * @param {Object} config
8918 addColumn : function(c)
8921 var i = this.config.length;
8924 if(typeof c.dataIndex == "undefined"){
8927 if(typeof c.renderer == "string"){
8928 c.renderer = Roo.util.Format[c.renderer];
8930 if(typeof c.id == "undefined"){
8933 if(c.editor && c.editor.xtype){
8934 c.editor = Roo.factory(c.editor, Roo.grid);
8936 if(c.editor && c.editor.isFormField){
8937 c.editor = new Roo.grid.GridEditor(c.editor);
8939 this.lookup[c.id] = c;
8944 Roo.grid.ColumnModel.defaultRenderer = function(value)
8946 if(typeof value == "object") {
8949 if(typeof value == "string" && value.length < 1){
8953 return String.format("{0}", value);
8956 // Alias for backwards compatibility
8957 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8960 * Ext JS Library 1.1.1
8961 * Copyright(c) 2006-2007, Ext JS, LLC.
8963 * Originally Released Under LGPL - original licence link has changed is not relivant.
8966 * <script type="text/javascript">
8970 * @class Roo.LoadMask
8971 * A simple utility class for generically masking elements while loading data. If the element being masked has
8972 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8973 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8974 * element's UpdateManager load indicator and will be destroyed after the initial load.
8976 * Create a new LoadMask
8977 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8978 * @param {Object} config The config object
8980 Roo.LoadMask = function(el, config){
8981 this.el = Roo.get(el);
8982 Roo.apply(this, config);
8984 this.store.on('beforeload', this.onBeforeLoad, this);
8985 this.store.on('load', this.onLoad, this);
8986 this.store.on('loadexception', this.onLoadException, this);
8987 this.removeMask = false;
8989 var um = this.el.getUpdateManager();
8990 um.showLoadIndicator = false; // disable the default indicator
8991 um.on('beforeupdate', this.onBeforeLoad, this);
8992 um.on('update', this.onLoad, this);
8993 um.on('failure', this.onLoad, this);
8994 this.removeMask = true;
8998 Roo.LoadMask.prototype = {
9000 * @cfg {Boolean} removeMask
9001 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9002 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9007 * The text to display in a centered loading message box (defaults to 'Loading...')
9011 * @cfg {String} msgCls
9012 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9014 msgCls : 'x-mask-loading',
9017 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9023 * Disables the mask to prevent it from being displayed
9025 disable : function(){
9026 this.disabled = true;
9030 * Enables the mask so that it can be displayed
9032 enable : function(){
9033 this.disabled = false;
9036 onLoadException : function()
9040 if (typeof(arguments[3]) != 'undefined') {
9041 Roo.MessageBox.alert("Error loading",arguments[3]);
9045 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9046 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9053 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9058 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9062 onBeforeLoad : function(){
9064 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9069 destroy : function(){
9071 this.store.un('beforeload', this.onBeforeLoad, this);
9072 this.store.un('load', this.onLoad, this);
9073 this.store.un('loadexception', this.onLoadException, this);
9075 var um = this.el.getUpdateManager();
9076 um.un('beforeupdate', this.onBeforeLoad, this);
9077 um.un('update', this.onLoad, this);
9078 um.un('failure', this.onLoad, this);
9082 * @class Roo.bootstrap.Table
9084 * @extends Roo.bootstrap.Component
9085 * @children Roo.bootstrap.TableBody
9086 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9087 * Similar to Roo.grid.Grid
9089 var table = Roo.factory({
9091 xns : Roo.bootstrap,
9092 autoSizeColumns: true,
9099 sortInfo : { direction : 'ASC', field: 'name' },
9101 xtype : 'HttpProxy',
9104 url : 'https://example.com/some.data.url.json'
9107 xtype : 'JsonReader',
9109 fields : [ 'id', 'name', whatever' ],
9116 xtype : 'ColumnModel',
9120 dataIndex : 'is_in_group',
9123 renderer : function(v, x , r) {
9125 return String.format("{0}", v)
9131 xtype : 'RowSelectionModel',
9132 xns : Roo.bootstrap.Table
9133 // you can add listeners to catch selection change here....
9139 grid.render(Roo.get("some-div"));
9142 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9147 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9148 * @cfg {Roo.data.Store} store The data store to use
9149 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9151 * @cfg {String} cls table class
9154 * @cfg {boolean} striped Should the rows be alternative striped
9155 * @cfg {boolean} bordered Add borders to the table
9156 * @cfg {boolean} hover Add hover highlighting
9157 * @cfg {boolean} condensed Format condensed
9158 * @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,
9159 * also adds table-responsive (see bootstrap docs for details)
9160 * @cfg {Boolean} loadMask (true|false) default false
9161 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9162 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9163 * @cfg {Boolean} rowSelection (true|false) default false
9164 * @cfg {Boolean} cellSelection (true|false) default false
9165 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
9166 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9167 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9168 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9169 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
9170 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9173 * Create a new Table
9174 * @param {Object} config The config object
9177 Roo.bootstrap.Table = function(config)
9179 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9182 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9183 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9184 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9185 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9187 this.view = this; // compat with grid.
9189 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9191 this.sm.grid = this;
9192 this.selModel = Roo.factory(this.sm, Roo.grid);
9193 this.sm = this.selModel;
9194 this.sm.xmodule = this.xmodule || false;
9197 if (this.cm && typeof(this.cm.config) == 'undefined') {
9198 this.colModel = new Roo.grid.ColumnModel(this.cm);
9199 this.cm = this.colModel;
9200 this.cm.xmodule = this.xmodule || false;
9203 this.store= Roo.factory(this.store, Roo.data);
9204 this.ds = this.store;
9205 this.ds.xmodule = this.xmodule || false;
9208 if (this.footer && this.store) {
9209 this.footer.dataSource = this.ds;
9210 this.footer = Roo.factory(this.footer);
9217 * Fires when a cell is clicked
9218 * @param {Roo.bootstrap.Table} this
9219 * @param {Roo.Element} el
9220 * @param {Number} rowIndex
9221 * @param {Number} columnIndex
9222 * @param {Roo.EventObject} e
9226 * @event celldblclick
9227 * Fires when a cell is double clicked
9228 * @param {Roo.bootstrap.Table} this
9229 * @param {Roo.Element} el
9230 * @param {Number} rowIndex
9231 * @param {Number} columnIndex
9232 * @param {Roo.EventObject} e
9234 "celldblclick" : true,
9237 * Fires when a row is clicked
9238 * @param {Roo.bootstrap.Table} this
9239 * @param {Roo.Element} el
9240 * @param {Number} rowIndex
9241 * @param {Roo.EventObject} e
9245 * @event rowdblclick
9246 * Fires when a row is double clicked
9247 * @param {Roo.bootstrap.Table} this
9248 * @param {Roo.Element} el
9249 * @param {Number} rowIndex
9250 * @param {Roo.EventObject} e
9252 "rowdblclick" : true,
9255 * Fires when a mouseover occur
9256 * @param {Roo.bootstrap.Table} this
9257 * @param {Roo.Element} el
9258 * @param {Number} rowIndex
9259 * @param {Number} columnIndex
9260 * @param {Roo.EventObject} e
9265 * Fires when a mouseout occur
9266 * @param {Roo.bootstrap.Table} this
9267 * @param {Roo.Element} el
9268 * @param {Number} rowIndex
9269 * @param {Number} columnIndex
9270 * @param {Roo.EventObject} e
9275 * Fires when a row is rendered, so you can change add a style to it.
9276 * @param {Roo.bootstrap.Table} this
9277 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9281 * @event rowsrendered
9282 * Fires when all the rows have been rendered
9283 * @param {Roo.bootstrap.Table} this
9285 'rowsrendered' : true,
9287 * @event contextmenu
9288 * The raw contextmenu event for the entire grid.
9289 * @param {Roo.EventObject} e
9291 "contextmenu" : true,
9293 * @event rowcontextmenu
9294 * Fires when a row is right clicked
9295 * @param {Roo.bootstrap.Table} this
9296 * @param {Number} rowIndex
9297 * @param {Roo.EventObject} e
9299 "rowcontextmenu" : true,
9301 * @event cellcontextmenu
9302 * Fires when a cell is right clicked
9303 * @param {Roo.bootstrap.Table} this
9304 * @param {Number} rowIndex
9305 * @param {Number} cellIndex
9306 * @param {Roo.EventObject} e
9308 "cellcontextmenu" : true,
9310 * @event headercontextmenu
9311 * Fires when a header is right clicked
9312 * @param {Roo.bootstrap.Table} this
9313 * @param {Number} columnIndex
9314 * @param {Roo.EventObject} e
9316 "headercontextmenu" : true,
9319 * The raw mousedown event for the entire grid.
9320 * @param {Roo.EventObject} e
9327 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9343 enableColumnResize: true,
9345 rowSelection : false,
9346 cellSelection : false,
9349 minColumnWidth : 50,
9351 // Roo.Element - the tbody
9352 bodyEl: false, // <tbody> Roo.Element - thead element
9353 headEl: false, // <thead> Roo.Element - thead element
9354 resizeProxy : false, // proxy element for dragging?
9358 container: false, // used by gridpanel...
9364 auto_hide_footer : false,
9366 view: false, // actually points to this..
9368 getAutoCreate : function()
9370 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9377 // this get's auto added by panel.Grid
9378 if (this.scrollBody) {
9379 cfg.cls += ' table-body-fixed';
9382 cfg.cls += ' table-striped';
9386 cfg.cls += ' table-hover';
9388 if (this.bordered) {
9389 cfg.cls += ' table-bordered';
9391 if (this.condensed) {
9392 cfg.cls += ' table-condensed';
9395 if (this.responsive) {
9396 cfg.cls += ' table-responsive';
9400 cfg.cls+= ' ' +this.cls;
9406 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9409 if(this.store || this.cm){
9410 if(this.headerShow){
9411 cfg.cn.push(this.renderHeader());
9414 cfg.cn.push(this.renderBody());
9416 if(this.footerShow){
9417 cfg.cn.push(this.renderFooter());
9419 // where does this come from?
9420 //cfg.cls+= ' TableGrid';
9423 return { cn : [ cfg ] };
9426 initEvents : function()
9428 if(!this.store || !this.cm){
9431 if (this.selModel) {
9432 this.selModel.initEvents();
9436 //Roo.log('initEvents with ds!!!!');
9438 this.bodyEl = this.el.select('tbody', true).first();
9439 this.headEl = this.el.select('thead', true).first();
9440 this.mainFoot = this.el.select('tfoot', true).first();
9445 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9446 e.on('click', this.sort, this);
9450 // why is this done????? = it breaks dialogs??
9451 //this.parent().el.setStyle('position', 'relative');
9455 this.footer.parentId = this.id;
9456 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9459 this.el.select('tfoot tr td').first().addClass('hide');
9464 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9467 this.store.on('load', this.onLoad, this);
9468 this.store.on('beforeload', this.onBeforeLoad, this);
9469 this.store.on('update', this.onUpdate, this);
9470 this.store.on('add', this.onAdd, this);
9471 this.store.on("clear", this.clear, this);
9473 this.el.on("contextmenu", this.onContextMenu, this);
9476 this.cm.on("headerchange", this.onHeaderChange, this);
9477 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9479 //?? does bodyEl get replaced on render?
9480 this.bodyEl.on("click", this.onClick, this);
9481 this.bodyEl.on("dblclick", this.onDblClick, this);
9482 this.bodyEl.on('scroll', this.onBodyScroll, this);
9484 // guessing mainbody will work - this relays usually caught by selmodel at present.
9485 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9488 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9491 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9492 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9497 // Compatibility with grid - we implement all the view features at present.
9498 getView : function()
9503 initCSS : function()
9507 var cm = this.cm, styles = [];
9508 this.CSS.removeStyleSheet(this.id + '-cssrules');
9509 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9510 // we can honour xs/sm/md/xl as widths...
9511 // we first have to decide what widht we are currently at...
9512 var sz = Roo.getGridSize();
9516 var cols = []; // visable cols.
9518 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9519 var w = cm.getColumnWidth(i, false);
9521 cols.push( { rel : false, abs : 0 });
9525 cols.push( { rel : false, abs : w });
9527 last = i; // not really..
9530 var w = cm.getColumnWidth(i, sz);
9535 cols.push( { rel : w, abs : false });
9538 var avail = this.bodyEl.dom.clientWidth - total_abs;
9540 var unitWidth = Math.floor(avail / total);
9541 var rem = avail - (unitWidth * total);
9543 var hidden, width, pos = 0 , splithide , left;
9544 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9546 hidden = 'display:none;';
9548 width = 'width:0px;';
9550 if(!cm.isHidden(i)){
9554 // we can honour xs/sm/md/xl ?
9555 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9557 hidden = 'display:none;';
9559 // width should return a small number...
9561 w+=rem; // add the remaining with..
9564 left = "left:" + (pos -4) + "px;";
9565 width = "width:" + w+ "px;";
9568 if (this.responsive) {
9571 hidden = cm.isHidden(i) ? 'display:none;' : '';
9572 splithide = 'display: none;';
9575 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9578 splithide = 'display:none;';
9581 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9582 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9587 //Roo.log(styles.join(''));
9588 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9594 onContextMenu : function(e, t)
9596 this.processEvent("contextmenu", e);
9599 processEvent : function(name, e)
9601 if (name != 'touchstart' ) {
9602 this.fireEvent(name, e);
9605 var t = e.getTarget();
9607 var cell = Roo.get(t);
9613 if(cell.findParent('tfoot', false, true)){
9617 if(cell.findParent('thead', false, true)){
9619 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9620 cell = Roo.get(t).findParent('th', false, true);
9622 Roo.log("failed to find th in thead?");
9623 Roo.log(e.getTarget());
9628 var cellIndex = cell.dom.cellIndex;
9630 var ename = name == 'touchstart' ? 'click' : name;
9631 this.fireEvent("header" + ename, this, cellIndex, e);
9636 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9637 cell = Roo.get(t).findParent('td', false, true);
9639 Roo.log("failed to find th in tbody?");
9640 Roo.log(e.getTarget());
9645 var row = cell.findParent('tr', false, true);
9646 var cellIndex = cell.dom.cellIndex;
9647 var rowIndex = row.dom.rowIndex - 1;
9651 this.fireEvent("row" + name, this, rowIndex, e);
9655 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9661 onMouseover : function(e, el)
9663 var cell = Roo.get(el);
9669 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9670 cell = cell.findParent('td', false, true);
9673 var row = cell.findParent('tr', false, true);
9674 var cellIndex = cell.dom.cellIndex;
9675 var rowIndex = row.dom.rowIndex - 1; // start from 0
9677 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9681 onMouseout : function(e, el)
9683 var cell = Roo.get(el);
9689 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9690 cell = cell.findParent('td', false, true);
9693 var row = cell.findParent('tr', false, true);
9694 var cellIndex = cell.dom.cellIndex;
9695 var rowIndex = row.dom.rowIndex - 1; // start from 0
9697 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9701 onClick : function(e, el)
9703 var cell = Roo.get(el);
9705 if(!cell || (!this.cellSelection && !this.rowSelection)){
9709 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9710 cell = cell.findParent('td', false, true);
9713 if(!cell || typeof(cell) == 'undefined'){
9717 var row = cell.findParent('tr', false, true);
9719 if(!row || typeof(row) == 'undefined'){
9723 var cellIndex = cell.dom.cellIndex;
9724 var rowIndex = this.getRowIndex(row);
9726 // why??? - should these not be based on SelectionModel?
9727 //if(this.cellSelection){
9728 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9731 //if(this.rowSelection){
9732 this.fireEvent('rowclick', this, row, rowIndex, e);
9737 onDblClick : function(e,el)
9739 var cell = Roo.get(el);
9741 if(!cell || (!this.cellSelection && !this.rowSelection)){
9745 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9746 cell = cell.findParent('td', false, true);
9749 if(!cell || typeof(cell) == 'undefined'){
9753 var row = cell.findParent('tr', false, true);
9755 if(!row || typeof(row) == 'undefined'){
9759 var cellIndex = cell.dom.cellIndex;
9760 var rowIndex = this.getRowIndex(row);
9762 if(this.cellSelection){
9763 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9766 if(this.rowSelection){
9767 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9770 findRowIndex : function(el)
9772 var cell = Roo.get(el);
9776 var row = cell.findParent('tr', false, true);
9778 if(!row || typeof(row) == 'undefined'){
9781 return this.getRowIndex(row);
9783 sort : function(e,el)
9785 var col = Roo.get(el);
9787 if(!col.hasClass('sortable')){
9791 var sort = col.attr('sort');
9794 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9798 this.store.sortInfo = {field : sort, direction : dir};
9801 Roo.log("calling footer first");
9802 this.footer.onClick('first');
9805 this.store.load({ params : { start : 0 } });
9809 renderHeader : function()
9817 this.totalWidth = 0;
9819 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9821 var config = cm.config[i];
9825 cls : 'x-hcol-' + i,
9828 html: cm.getColumnHeader(i)
9831 var tooltip = cm.getColumnTooltip(i);
9833 c.tooltip = tooltip;
9839 if(typeof(config.sortable) != 'undefined' && config.sortable){
9840 c.cls += ' sortable';
9841 c.html = '<i class="fa"></i>' + c.html;
9844 // could use BS4 hidden-..-down
9846 if(typeof(config.lgHeader) != 'undefined'){
9847 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9850 if(typeof(config.mdHeader) != 'undefined'){
9851 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9854 if(typeof(config.smHeader) != 'undefined'){
9855 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9858 if(typeof(config.xsHeader) != 'undefined'){
9859 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9866 if(typeof(config.tooltip) != 'undefined'){
9867 c.tooltip = config.tooltip;
9870 if(typeof(config.colspan) != 'undefined'){
9871 c.colspan = config.colspan;
9874 // hidden is handled by CSS now
9876 if(typeof(config.dataIndex) != 'undefined'){
9877 c.sort = config.dataIndex;
9882 if(typeof(config.align) != 'undefined' && config.align.length){
9883 c.style += ' text-align:' + config.align + ';';
9886 /* width is done in CSS
9887 *if(typeof(config.width) != 'undefined'){
9888 c.style += ' width:' + config.width + 'px;';
9889 this.totalWidth += config.width;
9891 this.totalWidth += 100; // assume minimum of 100 per column?
9895 if(typeof(config.cls) != 'undefined'){
9896 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9898 // this is the bit that doesnt reall work at all...
9900 if (this.responsive) {
9903 ['xs','sm','md','lg'].map(function(size){
9905 if(typeof(config[size]) == 'undefined'){
9909 if (!config[size]) { // 0 = hidden
9910 // BS 4 '0' is treated as hide that column and below.
9911 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9915 c.cls += ' col-' + size + '-' + config[size] + (
9916 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9924 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9935 renderBody : function()
9945 colspan : this.cm.getColumnCount()
9955 renderFooter : function()
9965 colspan : this.cm.getColumnCount()
9979 // Roo.log('ds onload');
9984 var ds = this.store;
9986 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9987 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9988 if (_this.store.sortInfo) {
9990 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9991 e.select('i', true).addClass(['fa-arrow-up']);
9994 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9995 e.select('i', true).addClass(['fa-arrow-down']);
10000 var tbody = this.bodyEl;
10002 if(ds.getCount() > 0){
10003 ds.data.each(function(d,rowIndex){
10004 var row = this.renderRow(cm, ds, rowIndex);
10006 tbody.createChild(row);
10010 if(row.cellObjects.length){
10011 Roo.each(row.cellObjects, function(r){
10012 _this.renderCellObject(r);
10019 var tfoot = this.el.select('tfoot', true).first();
10021 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10023 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10025 var total = this.ds.getTotalCount();
10027 if(this.footer.pageSize < total){
10028 this.mainFoot.show();
10032 Roo.each(this.el.select('tbody td', true).elements, function(e){
10033 e.on('mouseover', _this.onMouseover, _this);
10036 Roo.each(this.el.select('tbody td', true).elements, function(e){
10037 e.on('mouseout', _this.onMouseout, _this);
10039 this.fireEvent('rowsrendered', this);
10043 this.initCSS(); /// resize cols
10049 onUpdate : function(ds,record)
10051 this.refreshRow(record);
10055 onRemove : function(ds, record, index, isUpdate){
10056 if(isUpdate !== true){
10057 this.fireEvent("beforerowremoved", this, index, record);
10059 var bt = this.bodyEl.dom;
10061 var rows = this.el.select('tbody > tr', true).elements;
10063 if(typeof(rows[index]) != 'undefined'){
10064 bt.removeChild(rows[index].dom);
10067 // if(bt.rows[index]){
10068 // bt.removeChild(bt.rows[index]);
10071 if(isUpdate !== true){
10072 //this.stripeRows(index);
10073 //this.syncRowHeights(index, index);
10075 this.fireEvent("rowremoved", this, index, record);
10079 onAdd : function(ds, records, rowIndex)
10081 //Roo.log('on Add called');
10082 // - note this does not handle multiple adding very well..
10083 var bt = this.bodyEl.dom;
10084 for (var i =0 ; i < records.length;i++) {
10085 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10086 //Roo.log(records[i]);
10087 //Roo.log(this.store.getAt(rowIndex+i));
10088 this.insertRow(this.store, rowIndex + i, false);
10095 refreshRow : function(record){
10096 var ds = this.store, index;
10097 if(typeof record == 'number'){
10099 record = ds.getAt(index);
10101 index = ds.indexOf(record);
10103 return; // should not happen - but seems to
10106 this.insertRow(ds, index, true);
10108 this.onRemove(ds, record, index+1, true);
10110 //this.syncRowHeights(index, index);
10112 this.fireEvent("rowupdated", this, index, record);
10114 // private - called by RowSelection
10115 onRowSelect : function(rowIndex){
10116 var row = this.getRowDom(rowIndex);
10117 row.addClass(['bg-info','info']);
10119 // private - called by RowSelection
10120 onRowDeselect : function(rowIndex)
10122 if (rowIndex < 0) {
10125 var row = this.getRowDom(rowIndex);
10126 row.removeClass(['bg-info','info']);
10129 * Focuses the specified row.
10130 * @param {Number} row The row index
10132 focusRow : function(row)
10134 //Roo.log('GridView.focusRow');
10135 var x = this.bodyEl.dom.scrollLeft;
10136 this.focusCell(row, 0, false);
10137 this.bodyEl.dom.scrollLeft = x;
10141 * Focuses the specified cell.
10142 * @param {Number} row The row index
10143 * @param {Number} col The column index
10144 * @param {Boolean} hscroll false to disable horizontal scrolling
10146 focusCell : function(row, col, hscroll)
10148 //Roo.log('GridView.focusCell');
10149 var el = this.ensureVisible(row, col, hscroll);
10150 // not sure what focusEL achives = it's a <a> pos relative
10151 //this.focusEl.alignTo(el, "tl-tl");
10153 // this.focusEl.focus();
10155 // this.focusEl.focus.defer(1, this.focusEl);
10160 * Scrolls the specified cell into view
10161 * @param {Number} row The row index
10162 * @param {Number} col The column index
10163 * @param {Boolean} hscroll false to disable horizontal scrolling
10165 ensureVisible : function(row, col, hscroll)
10167 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10168 //return null; //disable for testing.
10169 if(typeof row != "number"){
10170 row = row.rowIndex;
10172 if(row < 0 && row >= this.ds.getCount()){
10175 col = (col !== undefined ? col : 0);
10177 while(cm.isHidden(col)){
10181 var el = this.getCellDom(row, col);
10185 var c = this.bodyEl.dom;
10187 var ctop = parseInt(el.offsetTop, 10);
10188 var cleft = parseInt(el.offsetLeft, 10);
10189 var cbot = ctop + el.offsetHeight;
10190 var cright = cleft + el.offsetWidth;
10192 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10193 var ch = 0; //?? header is not withing the area?
10194 var stop = parseInt(c.scrollTop, 10);
10195 var sleft = parseInt(c.scrollLeft, 10);
10196 var sbot = stop + ch;
10197 var sright = sleft + c.clientWidth;
10199 Roo.log('GridView.ensureVisible:' +
10201 ' c.clientHeight:' + c.clientHeight +
10202 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10210 c.scrollTop = ctop;
10211 //Roo.log("set scrolltop to ctop DISABLE?");
10212 }else if(cbot > sbot){
10213 //Roo.log("set scrolltop to cbot-ch");
10214 c.scrollTop = cbot-ch;
10217 if(hscroll !== false){
10219 c.scrollLeft = cleft;
10220 }else if(cright > sright){
10221 c.scrollLeft = cright-c.clientWidth;
10229 insertRow : function(dm, rowIndex, isUpdate){
10232 this.fireEvent("beforerowsinserted", this, rowIndex);
10234 //var s = this.getScrollState();
10235 var row = this.renderRow(this.cm, this.store, rowIndex);
10236 // insert before rowIndex..
10237 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10241 if(row.cellObjects.length){
10242 Roo.each(row.cellObjects, function(r){
10243 _this.renderCellObject(r);
10248 this.fireEvent("rowsinserted", this, rowIndex);
10249 //this.syncRowHeights(firstRow, lastRow);
10250 //this.stripeRows(firstRow);
10257 getRowDom : function(rowIndex)
10259 var rows = this.el.select('tbody > tr', true).elements;
10261 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10264 getCellDom : function(rowIndex, colIndex)
10266 var row = this.getRowDom(rowIndex);
10267 if (row === false) {
10270 var cols = row.select('td', true).elements;
10271 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10275 // returns the object tree for a tr..
10278 renderRow : function(cm, ds, rowIndex)
10280 var d = ds.getAt(rowIndex);
10284 cls : 'x-row-' + rowIndex,
10288 var cellObjects = [];
10290 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10291 var config = cm.config[i];
10293 var renderer = cm.getRenderer(i);
10297 if(typeof(renderer) !== 'undefined'){
10298 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10300 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10301 // and are rendered into the cells after the row is rendered - using the id for the element.
10303 if(typeof(value) === 'object'){
10313 rowIndex : rowIndex,
10318 this.fireEvent('rowclass', this, rowcfg);
10322 // this might end up displaying HTML?
10323 // this is too messy... - better to only do it on columsn you know are going to be too long
10324 //tooltip : (typeof(value) === 'object') ? '' : value,
10325 cls : rowcfg.rowClass + ' x-col-' + i,
10327 html: (typeof(value) === 'object') ? '' : value
10334 if(typeof(config.colspan) != 'undefined'){
10335 td.colspan = config.colspan;
10340 if(typeof(config.align) != 'undefined' && config.align.length){
10341 td.style += ' text-align:' + config.align + ';';
10343 if(typeof(config.valign) != 'undefined' && config.valign.length){
10344 td.style += ' vertical-align:' + config.valign + ';';
10347 if(typeof(config.width) != 'undefined'){
10348 td.style += ' width:' + config.width + 'px;';
10352 if(typeof(config.cursor) != 'undefined'){
10353 td.style += ' cursor:' + config.cursor + ';';
10356 if(typeof(config.cls) != 'undefined'){
10357 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10359 if (this.responsive) {
10360 ['xs','sm','md','lg'].map(function(size){
10362 if(typeof(config[size]) == 'undefined'){
10368 if (!config[size]) { // 0 = hidden
10369 // BS 4 '0' is treated as hide that column and below.
10370 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10374 td.cls += ' col-' + size + '-' + config[size] + (
10375 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10385 row.cellObjects = cellObjects;
10393 onBeforeLoad : function()
10402 this.el.select('tbody', true).first().dom.innerHTML = '';
10405 * Show or hide a row.
10406 * @param {Number} rowIndex to show or hide
10407 * @param {Boolean} state hide
10409 setRowVisibility : function(rowIndex, state)
10411 var bt = this.bodyEl.dom;
10413 var rows = this.el.select('tbody > tr', true).elements;
10415 if(typeof(rows[rowIndex]) == 'undefined'){
10418 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10423 getSelectionModel : function(){
10424 if(!this.selModel){
10425 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10427 return this.selModel;
10430 * Render the Roo.bootstrap object from renderder
10432 renderCellObject : function(r)
10436 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10438 var t = r.cfg.render(r.container);
10441 Roo.each(r.cfg.cn, function(c){
10443 container: t.getChildContainer(),
10446 _this.renderCellObject(child);
10451 * get the Row Index from a dom element.
10452 * @param {Roo.Element} row The row to look for
10453 * @returns {Number} the row
10455 getRowIndex : function(row)
10459 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10470 * get the header TH element for columnIndex
10471 * @param {Number} columnIndex
10472 * @returns {Roo.Element}
10474 getHeaderIndex: function(colIndex)
10476 var cols = this.headEl.select('th', true).elements;
10477 return cols[colIndex];
10480 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10481 * @param {domElement} cell to look for
10482 * @returns {Number} the column
10484 getCellIndex : function(cell)
10486 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10488 return parseInt(id[1], 10);
10493 * Returns the grid's underlying element = used by panel.Grid
10494 * @return {Element} The element
10496 getGridEl : function(){
10500 * Forces a resize - used by panel.Grid
10501 * @return {Element} The element
10503 autoSize : function()
10505 //var ctr = Roo.get(this.container.dom.parentElement);
10506 var ctr = Roo.get(this.el.dom);
10508 var thd = this.getGridEl().select('thead',true).first();
10509 var tbd = this.getGridEl().select('tbody', true).first();
10510 var tfd = this.getGridEl().select('tfoot', true).first();
10512 var cw = ctr.getWidth();
10513 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10517 tbd.setWidth(ctr.getWidth());
10518 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10519 // this needs fixing for various usage - currently only hydra job advers I think..
10521 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10523 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10526 cw = Math.max(cw, this.totalWidth);
10527 this.getGridEl().select('tbody tr',true).setWidth(cw);
10530 // resize 'expandable coloumn?
10532 return; // we doe not have a view in this design..
10535 onBodyScroll: function()
10537 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10539 this.headEl.setStyle({
10540 'position' : 'relative',
10541 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10547 var scrollHeight = this.bodyEl.dom.scrollHeight;
10549 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10551 var height = this.bodyEl.getHeight();
10553 if(scrollHeight - height == scrollTop) {
10555 var total = this.ds.getTotalCount();
10557 if(this.footer.cursor + this.footer.pageSize < total){
10559 this.footer.ds.load({
10561 start : this.footer.cursor + this.footer.pageSize,
10562 limit : this.footer.pageSize
10571 onColumnSplitterMoved : function(i, diff)
10573 this.userResized = true;
10575 var cm = this.colModel;
10577 var w = this.getHeaderIndex(i).getWidth() + diff;
10580 cm.setColumnWidth(i, w, true);
10582 //var cid = cm.getColumnId(i); << not used in this version?
10583 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10585 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10586 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10587 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10589 //this.updateSplitters();
10590 //this.layout(); << ??
10591 this.fireEvent("columnresize", i, w);
10593 onHeaderChange : function()
10595 var header = this.renderHeader();
10596 var table = this.el.select('table', true).first();
10598 this.headEl.remove();
10599 this.headEl = table.createChild(header, this.bodyEl, false);
10601 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10602 e.on('click', this.sort, this);
10605 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10606 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10611 onHiddenChange : function(colModel, colIndex, hidden)
10614 this.cm.setHidden()
10615 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10616 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10618 this.CSS.updateRule(thSelector, "display", "");
10619 this.CSS.updateRule(tdSelector, "display", "");
10622 this.CSS.updateRule(thSelector, "display", "none");
10623 this.CSS.updateRule(tdSelector, "display", "none");
10626 // onload calls initCSS()
10627 this.onHeaderChange();
10631 setColumnWidth: function(col_index, width)
10633 // width = "md-2 xs-2..."
10634 if(!this.colModel.config[col_index]) {
10638 var w = width.split(" ");
10640 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10642 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10645 for(var j = 0; j < w.length; j++) {
10651 var size_cls = w[j].split("-");
10653 if(!Number.isInteger(size_cls[1] * 1)) {
10657 if(!this.colModel.config[col_index][size_cls[0]]) {
10661 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10665 h_row[0].classList.replace(
10666 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10667 "col-"+size_cls[0]+"-"+size_cls[1]
10670 for(var i = 0; i < rows.length; i++) {
10672 var size_cls = w[j].split("-");
10674 if(!Number.isInteger(size_cls[1] * 1)) {
10678 if(!this.colModel.config[col_index][size_cls[0]]) {
10682 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10686 rows[i].classList.replace(
10687 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10688 "col-"+size_cls[0]+"-"+size_cls[1]
10692 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10697 // currently only used to find the split on drag..
10698 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10703 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10704 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10713 * @class Roo.bootstrap.TableCell
10714 * @extends Roo.bootstrap.Component
10715 * @children Roo.bootstrap.Component
10716 * @parent Roo.bootstrap.TableRow
10717 * Bootstrap TableCell class
10719 * @cfg {String} html cell contain text
10720 * @cfg {String} cls cell class
10721 * @cfg {String} tag cell tag (td|th) default td
10722 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10723 * @cfg {String} align Aligns the content in a cell
10724 * @cfg {String} axis Categorizes cells
10725 * @cfg {String} bgcolor Specifies the background color of a cell
10726 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10727 * @cfg {Number} colspan Specifies the number of columns a cell should span
10728 * @cfg {String} headers Specifies one or more header cells a cell is related to
10729 * @cfg {Number} height Sets the height of a cell
10730 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10731 * @cfg {Number} rowspan Sets the number of rows a cell should span
10732 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10733 * @cfg {String} valign Vertical aligns the content in a cell
10734 * @cfg {Number} width Specifies the width of a cell
10737 * Create a new TableCell
10738 * @param {Object} config The config object
10741 Roo.bootstrap.TableCell = function(config){
10742 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10745 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10765 getAutoCreate : function(){
10766 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10773 cfg.tag = this.tag;
10786 cfg.align=this.align
10791 if (this.bgcolor) {
10792 cfg.bgcolor=this.bgcolor
10794 if (this.charoff) {
10795 cfg.charoff=this.charoff
10797 if (this.colspan) {
10798 cfg.colspan=this.colspan
10800 if (this.headers) {
10801 cfg.headers=this.headers
10804 cfg.height=this.height
10807 cfg.nowrap=this.nowrap
10809 if (this.rowspan) {
10810 cfg.rowspan=this.rowspan
10813 cfg.scope=this.scope
10816 cfg.valign=this.valign
10819 cfg.width=this.width
10838 * @class Roo.bootstrap.TableRow
10839 * @extends Roo.bootstrap.Component
10840 * @children Roo.bootstrap.TableCell
10841 * @parent Roo.bootstrap.TableBody
10842 * Bootstrap TableRow class
10843 * @cfg {String} cls row class
10844 * @cfg {String} align Aligns the content in a table row
10845 * @cfg {String} bgcolor Specifies a background color for a table row
10846 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10847 * @cfg {String} valign Vertical aligns the content in a table row
10850 * Create a new TableRow
10851 * @param {Object} config The config object
10854 Roo.bootstrap.TableRow = function(config){
10855 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10858 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10866 getAutoCreate : function(){
10867 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10874 cfg.cls = this.cls;
10877 cfg.align = this.align;
10880 cfg.bgcolor = this.bgcolor;
10883 cfg.charoff = this.charoff;
10886 cfg.valign = this.valign;
10904 * @class Roo.bootstrap.TableBody
10905 * @extends Roo.bootstrap.Component
10906 * @children Roo.bootstrap.TableRow
10907 * @parent Roo.bootstrap.Table
10908 * Bootstrap TableBody class
10909 * @cfg {String} cls element class
10910 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10911 * @cfg {String} align Aligns the content inside the element
10912 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10913 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10916 * Create a new TableBody
10917 * @param {Object} config The config object
10920 Roo.bootstrap.TableBody = function(config){
10921 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10924 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10932 getAutoCreate : function(){
10933 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10943 cfg.tag = this.tag;
10947 cfg.align = this.align;
10950 cfg.charoff = this.charoff;
10953 cfg.valign = this.valign;
10960 // initEvents : function()
10963 // if(!this.store){
10967 // this.store = Roo.factory(this.store, Roo.data);
10968 // this.store.on('load', this.onLoad, this);
10970 // this.store.load();
10974 // onLoad: function ()
10976 // this.fireEvent('load', this);
10986 * Ext JS Library 1.1.1
10987 * Copyright(c) 2006-2007, Ext JS, LLC.
10989 * Originally Released Under LGPL - original licence link has changed is not relivant.
10992 * <script type="text/javascript">
10995 // as we use this in bootstrap.
10996 Roo.namespace('Roo.form');
10998 * @class Roo.form.Action
10999 * Internal Class used to handle form actions
11001 * @param {Roo.form.BasicForm} el The form element or its id
11002 * @param {Object} config Configuration options
11007 // define the action interface
11008 Roo.form.Action = function(form, options){
11010 this.options = options || {};
11013 * Client Validation Failed
11016 Roo.form.Action.CLIENT_INVALID = 'client';
11018 * Server Validation Failed
11021 Roo.form.Action.SERVER_INVALID = 'server';
11023 * Connect to Server Failed
11026 Roo.form.Action.CONNECT_FAILURE = 'connect';
11028 * Reading Data from Server Failed
11031 Roo.form.Action.LOAD_FAILURE = 'load';
11033 Roo.form.Action.prototype = {
11035 failureType : undefined,
11036 response : undefined,
11037 result : undefined,
11039 // interface method
11040 run : function(options){
11044 // interface method
11045 success : function(response){
11049 // interface method
11050 handleResponse : function(response){
11054 // default connection failure
11055 failure : function(response){
11057 this.response = response;
11058 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11059 this.form.afterAction(this, false);
11062 processResponse : function(response){
11063 this.response = response;
11064 if(!response.responseText){
11067 this.result = this.handleResponse(response);
11068 return this.result;
11071 // utility functions used internally
11072 getUrl : function(appendParams){
11073 var url = this.options.url || this.form.url || this.form.el.dom.action;
11075 var p = this.getParams();
11077 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11083 getMethod : function(){
11084 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11087 getParams : function(){
11088 var bp = this.form.baseParams;
11089 var p = this.options.params;
11091 if(typeof p == "object"){
11092 p = Roo.urlEncode(Roo.applyIf(p, bp));
11093 }else if(typeof p == 'string' && bp){
11094 p += '&' + Roo.urlEncode(bp);
11097 p = Roo.urlEncode(bp);
11102 createCallback : function(){
11104 success: this.success,
11105 failure: this.failure,
11107 timeout: (this.form.timeout*1000),
11108 upload: this.form.fileUpload ? this.success : undefined
11113 Roo.form.Action.Submit = function(form, options){
11114 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11117 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11120 haveProgress : false,
11121 uploadComplete : false,
11123 // uploadProgress indicator.
11124 uploadProgress : function()
11126 if (!this.form.progressUrl) {
11130 if (!this.haveProgress) {
11131 Roo.MessageBox.progress("Uploading", "Uploading");
11133 if (this.uploadComplete) {
11134 Roo.MessageBox.hide();
11138 this.haveProgress = true;
11140 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11142 var c = new Roo.data.Connection();
11144 url : this.form.progressUrl,
11149 success : function(req){
11150 //console.log(data);
11154 rdata = Roo.decode(req.responseText)
11156 Roo.log("Invalid data from server..");
11160 if (!rdata || !rdata.success) {
11162 Roo.MessageBox.alert(Roo.encode(rdata));
11165 var data = rdata.data;
11167 if (this.uploadComplete) {
11168 Roo.MessageBox.hide();
11173 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11174 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11177 this.uploadProgress.defer(2000,this);
11180 failure: function(data) {
11181 Roo.log('progress url failed ');
11192 // run get Values on the form, so it syncs any secondary forms.
11193 this.form.getValues();
11195 var o = this.options;
11196 var method = this.getMethod();
11197 var isPost = method == 'POST';
11198 if(o.clientValidation === false || this.form.isValid()){
11200 if (this.form.progressUrl) {
11201 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11202 (new Date() * 1) + '' + Math.random());
11207 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11208 form:this.form.el.dom,
11209 url:this.getUrl(!isPost),
11211 params:isPost ? this.getParams() : null,
11212 isUpload: this.form.fileUpload,
11213 formData : this.form.formData
11216 this.uploadProgress();
11218 }else if (o.clientValidation !== false){ // client validation failed
11219 this.failureType = Roo.form.Action.CLIENT_INVALID;
11220 this.form.afterAction(this, false);
11224 success : function(response)
11226 this.uploadComplete= true;
11227 if (this.haveProgress) {
11228 Roo.MessageBox.hide();
11232 var result = this.processResponse(response);
11233 if(result === true || result.success){
11234 this.form.afterAction(this, true);
11238 this.form.markInvalid(result.errors);
11239 this.failureType = Roo.form.Action.SERVER_INVALID;
11241 this.form.afterAction(this, false);
11243 failure : function(response)
11245 this.uploadComplete= true;
11246 if (this.haveProgress) {
11247 Roo.MessageBox.hide();
11250 this.response = response;
11251 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11252 this.form.afterAction(this, false);
11255 handleResponse : function(response){
11256 if(this.form.errorReader){
11257 var rs = this.form.errorReader.read(response);
11260 for(var i = 0, len = rs.records.length; i < len; i++) {
11261 var r = rs.records[i];
11262 errors[i] = r.data;
11265 if(errors.length < 1){
11269 success : rs.success,
11275 ret = Roo.decode(response.responseText);
11279 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11289 Roo.form.Action.Load = function(form, options){
11290 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11291 this.reader = this.form.reader;
11294 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11299 Roo.Ajax.request(Roo.apply(
11300 this.createCallback(), {
11301 method:this.getMethod(),
11302 url:this.getUrl(false),
11303 params:this.getParams()
11307 success : function(response){
11309 var result = this.processResponse(response);
11310 if(result === true || !result.success || !result.data){
11311 this.failureType = Roo.form.Action.LOAD_FAILURE;
11312 this.form.afterAction(this, false);
11315 this.form.clearInvalid();
11316 this.form.setValues(result.data);
11317 this.form.afterAction(this, true);
11320 handleResponse : function(response){
11321 if(this.form.reader){
11322 var rs = this.form.reader.read(response);
11323 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11325 success : rs.success,
11329 return Roo.decode(response.responseText);
11333 Roo.form.Action.ACTION_TYPES = {
11334 'load' : Roo.form.Action.Load,
11335 'submit' : Roo.form.Action.Submit
11344 * @class Roo.bootstrap.Form
11345 * @extends Roo.bootstrap.Component
11346 * @children Roo.bootstrap.Component
11347 * Bootstrap Form class
11348 * @cfg {String} method GET | POST (default POST)
11349 * @cfg {String} labelAlign top | left (default top)
11350 * @cfg {String} align left | right - for navbars
11351 * @cfg {Boolean} loadMask load mask when submit (default true)
11355 * Create a new Form
11356 * @param {Object} config The config object
11360 Roo.bootstrap.Form = function(config){
11362 Roo.bootstrap.Form.superclass.constructor.call(this, config);
11364 Roo.bootstrap.Form.popover.apply();
11368 * @event clientvalidation
11369 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11370 * @param {Form} this
11371 * @param {Boolean} valid true if the form has passed client-side validation
11373 clientvalidation: true,
11375 * @event beforeaction
11376 * Fires before any action is performed. Return false to cancel the action.
11377 * @param {Form} this
11378 * @param {Action} action The action to be performed
11380 beforeaction: true,
11382 * @event actionfailed
11383 * Fires when an action fails.
11384 * @param {Form} this
11385 * @param {Action} action The action that failed
11387 actionfailed : true,
11389 * @event actioncomplete
11390 * Fires when an action is completed.
11391 * @param {Form} this
11392 * @param {Action} action The action that completed
11394 actioncomplete : true
11398 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
11401 * @cfg {String} method
11402 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11406 * @cfg {String} url
11407 * The URL to use for form actions if one isn't supplied in the action options.
11410 * @cfg {Boolean} fileUpload
11411 * Set to true if this form is a file upload.
11415 * @cfg {Object} baseParams
11416 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11420 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11424 * @cfg {Sting} align (left|right) for navbar forms
11429 activeAction : null,
11432 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11433 * element by passing it or its id or mask the form itself by passing in true.
11436 waitMsgTarget : false,
11441 * @cfg {Boolean} errorMask (true|false) default false
11446 * @cfg {Number} maskOffset Default 100
11451 * @cfg {Boolean} maskBody
11455 getAutoCreate : function(){
11459 method : this.method || 'POST',
11460 id : this.id || Roo.id(),
11463 if (this.parent().xtype.match(/^Nav/)) {
11464 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11468 if (this.labelAlign == 'left' ) {
11469 cfg.cls += ' form-horizontal';
11475 initEvents : function()
11477 this.el.on('submit', this.onSubmit, this);
11478 // this was added as random key presses on the form where triggering form submit.
11479 this.el.on('keypress', function(e) {
11480 if (e.getCharCode() != 13) {
11483 // we might need to allow it for textareas.. and some other items.
11484 // check e.getTarget().
11486 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11490 Roo.log("keypress blocked");
11492 e.preventDefault();
11498 onSubmit : function(e){
11503 * Returns true if client-side validation on the form is successful.
11506 isValid : function(){
11507 var items = this.getItems();
11509 var target = false;
11511 items.each(function(f){
11517 Roo.log('invalid field: ' + f.name);
11521 if(!target && f.el.isVisible(true)){
11527 if(this.errorMask && !valid){
11528 Roo.bootstrap.Form.popover.mask(this, target);
11535 * Returns true if any fields in this form have changed since their original load.
11538 isDirty : function(){
11540 var items = this.getItems();
11541 items.each(function(f){
11551 * Performs a predefined action (submit or load) or custom actions you define on this form.
11552 * @param {String} actionName The name of the action type
11553 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11554 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11555 * accept other config options):
11557 Property Type Description
11558 ---------------- --------------- ----------------------------------------------------------------------------------
11559 url String The url for the action (defaults to the form's url)
11560 method String The form method to use (defaults to the form's method, or POST if not defined)
11561 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11562 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11563 validate the form on the client (defaults to false)
11565 * @return {BasicForm} this
11567 doAction : function(action, options){
11568 if(typeof action == 'string'){
11569 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11571 if(this.fireEvent('beforeaction', this, action) !== false){
11572 this.beforeAction(action);
11573 action.run.defer(100, action);
11579 beforeAction : function(action){
11580 var o = action.options;
11585 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11587 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11590 // not really supported yet.. ??
11592 //if(this.waitMsgTarget === true){
11593 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11594 //}else if(this.waitMsgTarget){
11595 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11596 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11598 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11604 afterAction : function(action, success){
11605 this.activeAction = null;
11606 var o = action.options;
11611 Roo.get(document.body).unmask();
11617 //if(this.waitMsgTarget === true){
11618 // this.el.unmask();
11619 //}else if(this.waitMsgTarget){
11620 // this.waitMsgTarget.unmask();
11622 // Roo.MessageBox.updateProgress(1);
11623 // Roo.MessageBox.hide();
11630 Roo.callback(o.success, o.scope, [this, action]);
11631 this.fireEvent('actioncomplete', this, action);
11635 // failure condition..
11636 // we have a scenario where updates need confirming.
11637 // eg. if a locking scenario exists..
11638 // we look for { errors : { needs_confirm : true }} in the response.
11640 (typeof(action.result) != 'undefined') &&
11641 (typeof(action.result.errors) != 'undefined') &&
11642 (typeof(action.result.errors.needs_confirm) != 'undefined')
11645 Roo.log("not supported yet");
11648 Roo.MessageBox.confirm(
11649 "Change requires confirmation",
11650 action.result.errorMsg,
11655 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11665 Roo.callback(o.failure, o.scope, [this, action]);
11666 // show an error message if no failed handler is set..
11667 if (!this.hasListener('actionfailed')) {
11668 Roo.log("need to add dialog support");
11670 Roo.MessageBox.alert("Error",
11671 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11672 action.result.errorMsg :
11673 "Saving Failed, please check your entries or try again"
11678 this.fireEvent('actionfailed', this, action);
11683 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11684 * @param {String} id The value to search for
11687 findField : function(id){
11688 var items = this.getItems();
11689 var field = items.get(id);
11691 items.each(function(f){
11692 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11699 return field || null;
11702 * Mark fields in this form invalid in bulk.
11703 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11704 * @return {BasicForm} this
11706 markInvalid : function(errors){
11707 if(errors instanceof Array){
11708 for(var i = 0, len = errors.length; i < len; i++){
11709 var fieldError = errors[i];
11710 var f = this.findField(fieldError.id);
11712 f.markInvalid(fieldError.msg);
11718 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11719 field.markInvalid(errors[id]);
11723 //Roo.each(this.childForms || [], function (f) {
11724 // f.markInvalid(errors);
11731 * Set values for fields in this form in bulk.
11732 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11733 * @return {BasicForm} this
11735 setValues : function(values){
11736 if(values instanceof Array){ // array of objects
11737 for(var i = 0, len = values.length; i < len; i++){
11739 var f = this.findField(v.id);
11741 f.setValue(v.value);
11742 if(this.trackResetOnLoad){
11743 f.originalValue = f.getValue();
11747 }else{ // object hash
11750 if(typeof values[id] != 'function' && (field = this.findField(id))){
11752 if (field.setFromData &&
11753 field.valueField &&
11754 field.displayField &&
11755 // combos' with local stores can
11756 // be queried via setValue()
11757 // to set their value..
11758 (field.store && !field.store.isLocal)
11762 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11763 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11764 field.setFromData(sd);
11766 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11768 field.setFromData(values);
11771 field.setValue(values[id]);
11775 if(this.trackResetOnLoad){
11776 field.originalValue = field.getValue();
11782 //Roo.each(this.childForms || [], function (f) {
11783 // f.setValues(values);
11790 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11791 * they are returned as an array.
11792 * @param {Boolean} asString
11795 getValues : function(asString){
11796 //if (this.childForms) {
11797 // copy values from the child forms
11798 // Roo.each(this.childForms, function (f) {
11799 // this.setValues(f.getValues());
11805 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11806 if(asString === true){
11809 return Roo.urlDecode(fs);
11813 * Returns the fields in this form as an object with key/value pairs.
11814 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11817 getFieldValues : function(with_hidden)
11819 var items = this.getItems();
11821 items.each(function(f){
11823 if (!f.getName()) {
11827 var v = f.getValue();
11829 if (f.inputType =='radio') {
11830 if (typeof(ret[f.getName()]) == 'undefined') {
11831 ret[f.getName()] = ''; // empty..
11834 if (!f.el.dom.checked) {
11838 v = f.el.dom.value;
11842 if(f.xtype == 'MoneyField'){
11843 ret[f.currencyName] = f.getCurrency();
11846 // not sure if this supported any more..
11847 if ((typeof(v) == 'object') && f.getRawValue) {
11848 v = f.getRawValue() ; // dates..
11850 // combo boxes where name != hiddenName...
11851 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11852 ret[f.name] = f.getRawValue();
11854 ret[f.getName()] = v;
11861 * Clears all invalid messages in this form.
11862 * @return {BasicForm} this
11864 clearInvalid : function(){
11865 var items = this.getItems();
11867 items.each(function(f){
11875 * Resets this form.
11876 * @return {BasicForm} this
11878 reset : function(){
11879 var items = this.getItems();
11880 items.each(function(f){
11884 Roo.each(this.childForms || [], function (f) {
11892 getItems : function()
11894 var r=new Roo.util.MixedCollection(false, function(o){
11895 return o.id || (o.id = Roo.id());
11897 var iter = function(el) {
11904 Roo.each(el.items,function(e) {
11913 hideFields : function(items)
11915 Roo.each(items, function(i){
11917 var f = this.findField(i);
11928 showFields : function(items)
11930 Roo.each(items, function(i){
11932 var f = this.findField(i);
11945 Roo.apply(Roo.bootstrap.Form, {
11961 intervalID : false,
11967 if(this.isApplied){
11972 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11973 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11974 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11975 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11978 this.maskEl.top.enableDisplayMode("block");
11979 this.maskEl.left.enableDisplayMode("block");
11980 this.maskEl.bottom.enableDisplayMode("block");
11981 this.maskEl.right.enableDisplayMode("block");
11983 this.toolTip = new Roo.bootstrap.Tooltip({
11984 cls : 'roo-form-error-popover',
11986 'left' : ['r-l', [-2,0], 'right'],
11987 'right' : ['l-r', [2,0], 'left'],
11988 'bottom' : ['tl-bl', [0,2], 'top'],
11989 'top' : [ 'bl-tl', [0,-2], 'bottom']
11993 this.toolTip.render(Roo.get(document.body));
11995 this.toolTip.el.enableDisplayMode("block");
11997 Roo.get(document.body).on('click', function(){
12001 Roo.get(document.body).on('touchstart', function(){
12005 this.isApplied = true
12008 mask : function(form, target)
12012 this.target = target;
12014 if(!this.form.errorMask || !target.el){
12018 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12020 Roo.log(scrollable);
12022 var ot = this.target.el.calcOffsetsTo(scrollable);
12024 var scrollTo = ot[1] - this.form.maskOffset;
12026 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12028 scrollable.scrollTo('top', scrollTo);
12030 var box = this.target.el.getBox();
12032 var zIndex = Roo.bootstrap.Modal.zIndex++;
12035 this.maskEl.top.setStyle('position', 'absolute');
12036 this.maskEl.top.setStyle('z-index', zIndex);
12037 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12038 this.maskEl.top.setLeft(0);
12039 this.maskEl.top.setTop(0);
12040 this.maskEl.top.show();
12042 this.maskEl.left.setStyle('position', 'absolute');
12043 this.maskEl.left.setStyle('z-index', zIndex);
12044 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12045 this.maskEl.left.setLeft(0);
12046 this.maskEl.left.setTop(box.y - this.padding);
12047 this.maskEl.left.show();
12049 this.maskEl.bottom.setStyle('position', 'absolute');
12050 this.maskEl.bottom.setStyle('z-index', zIndex);
12051 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12052 this.maskEl.bottom.setLeft(0);
12053 this.maskEl.bottom.setTop(box.bottom + this.padding);
12054 this.maskEl.bottom.show();
12056 this.maskEl.right.setStyle('position', 'absolute');
12057 this.maskEl.right.setStyle('z-index', zIndex);
12058 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12059 this.maskEl.right.setLeft(box.right + this.padding);
12060 this.maskEl.right.setTop(box.y - this.padding);
12061 this.maskEl.right.show();
12063 this.toolTip.bindEl = this.target.el;
12065 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12067 var tip = this.target.blankText;
12069 if(this.target.getValue() !== '' ) {
12071 if (this.target.invalidText.length) {
12072 tip = this.target.invalidText;
12073 } else if (this.target.regexText.length){
12074 tip = this.target.regexText;
12078 this.toolTip.show(tip);
12080 this.intervalID = window.setInterval(function() {
12081 Roo.bootstrap.Form.popover.unmask();
12084 window.onwheel = function(){ return false;};
12086 (function(){ this.isMasked = true; }).defer(500, this);
12090 unmask : function()
12092 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12096 this.maskEl.top.setStyle('position', 'absolute');
12097 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12098 this.maskEl.top.hide();
12100 this.maskEl.left.setStyle('position', 'absolute');
12101 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12102 this.maskEl.left.hide();
12104 this.maskEl.bottom.setStyle('position', 'absolute');
12105 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12106 this.maskEl.bottom.hide();
12108 this.maskEl.right.setStyle('position', 'absolute');
12109 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12110 this.maskEl.right.hide();
12112 this.toolTip.hide();
12114 this.toolTip.el.hide();
12116 window.onwheel = function(){ return true;};
12118 if(this.intervalID){
12119 window.clearInterval(this.intervalID);
12120 this.intervalID = false;
12123 this.isMasked = false;
12133 * Ext JS Library 1.1.1
12134 * Copyright(c) 2006-2007, Ext JS, LLC.
12136 * Originally Released Under LGPL - original licence link has changed is not relivant.
12139 * <script type="text/javascript">
12142 * @class Roo.form.VTypes
12143 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12146 Roo.form.VTypes = function(){
12147 // closure these in so they are only created once.
12148 var alpha = /^[a-zA-Z_]+$/;
12149 var alphanum = /^[a-zA-Z0-9_]+$/;
12150 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12151 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12153 // All these messages and functions are configurable
12156 * The function used to validate email addresses
12157 * @param {String} value The email address
12159 'email' : function(v){
12160 return email.test(v);
12163 * The error text to display when the email validation function returns false
12166 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12168 * The keystroke filter mask to be applied on email input
12171 'emailMask' : /[a-z0-9_\.\-@]/i,
12174 * The function used to validate URLs
12175 * @param {String} value The URL
12177 'url' : function(v){
12178 return url.test(v);
12181 * The error text to display when the url validation function returns false
12184 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12187 * The function used to validate alpha values
12188 * @param {String} value The value
12190 'alpha' : function(v){
12191 return alpha.test(v);
12194 * The error text to display when the alpha validation function returns false
12197 'alphaText' : 'This field should only contain letters and _',
12199 * The keystroke filter mask to be applied on alpha input
12202 'alphaMask' : /[a-z_]/i,
12205 * The function used to validate alphanumeric values
12206 * @param {String} value The value
12208 'alphanum' : function(v){
12209 return alphanum.test(v);
12212 * The error text to display when the alphanumeric validation function returns false
12215 'alphanumText' : 'This field should only contain letters, numbers and _',
12217 * The keystroke filter mask to be applied on alphanumeric input
12220 'alphanumMask' : /[a-z0-9_]/i
12230 * @class Roo.bootstrap.Input
12231 * @extends Roo.bootstrap.Component
12232 * Bootstrap Input class
12233 * @cfg {Boolean} disabled is it disabled
12234 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12235 * @cfg {String} name name of the input
12236 * @cfg {string} fieldLabel - the label associated
12237 * @cfg {string} placeholder - placeholder to put in text.
12238 * @cfg {string} before - input group add on before
12239 * @cfg {string} after - input group add on after
12240 * @cfg {string} size - (lg|sm) or leave empty..
12241 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12242 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12243 * @cfg {Number} md colspan out of 12 for computer-sized screens
12244 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12245 * @cfg {string} value default value of the input
12246 * @cfg {Number} labelWidth set the width of label
12247 * @cfg {Number} labellg set the width of label (1-12)
12248 * @cfg {Number} labelmd set the width of label (1-12)
12249 * @cfg {Number} labelsm set the width of label (1-12)
12250 * @cfg {Number} labelxs set the width of label (1-12)
12251 * @cfg {String} labelAlign (top|left)
12252 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12253 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12254 * @cfg {String} indicatorpos (left|right) default left
12255 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12256 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12257 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12258 * @cfg {Roo.bootstrap.Button} before Button to show before
12259 * @cfg {Roo.bootstrap.Button} afterButton to show before
12260 * @cfg {String} align (left|center|right) Default left
12261 * @cfg {Boolean} forceFeedback (true|false) Default false
12264 * Create a new Input
12265 * @param {Object} config The config object
12268 Roo.bootstrap.Input = function(config){
12270 Roo.bootstrap.Input.superclass.constructor.call(this, config);
12275 * Fires when this field receives input focus.
12276 * @param {Roo.form.Field} this
12281 * Fires when this field loses input focus.
12282 * @param {Roo.form.Field} this
12286 * @event specialkey
12287 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12288 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12289 * @param {Roo.form.Field} this
12290 * @param {Roo.EventObject} e The event object
12295 * Fires just before the field blurs if the field value has changed.
12296 * @param {Roo.form.Field} this
12297 * @param {Mixed} newValue The new value
12298 * @param {Mixed} oldValue The original value
12303 * Fires after the field has been marked as invalid.
12304 * @param {Roo.form.Field} this
12305 * @param {String} msg The validation message
12310 * Fires after the field has been validated with no errors.
12311 * @param {Roo.form.Field} this
12316 * Fires after the key up
12317 * @param {Roo.form.Field} this
12318 * @param {Roo.EventObject} e The event Object
12323 * Fires after the user pastes into input
12324 * @param {Roo.form.Field} this
12325 * @param {Roo.EventObject} e The event Object
12331 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
12333 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12334 automatic validation (defaults to "keyup").
12336 validationEvent : "keyup",
12338 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12340 validateOnBlur : true,
12342 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12344 validationDelay : 250,
12346 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12348 focusClass : "x-form-focus", // not needed???
12352 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12354 invalidClass : "has-warning",
12357 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12359 validClass : "has-success",
12362 * @cfg {Boolean} hasFeedback (true|false) default true
12364 hasFeedback : true,
12367 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12369 invalidFeedbackClass : "glyphicon-warning-sign",
12372 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12374 validFeedbackClass : "glyphicon-ok",
12377 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12379 selectOnFocus : false,
12382 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12386 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12391 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12393 disableKeyFilter : false,
12396 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12400 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12404 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12406 blankText : "Please complete this mandatory field",
12409 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12413 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12415 maxLength : Number.MAX_VALUE,
12417 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12419 minLengthText : "The minimum length for this field is {0}",
12421 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12423 maxLengthText : "The maximum length for this field is {0}",
12427 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12428 * If available, this function will be called only after the basic validators all return true, and will be passed the
12429 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12433 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12434 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12435 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12439 * @cfg {String} regexText -- Depricated - use Invalid Text
12444 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12450 autocomplete: false,
12454 inputType : 'text',
12457 placeholder: false,
12462 preventMark: false,
12463 isFormField : true,
12466 labelAlign : false,
12469 formatedValue : false,
12470 forceFeedback : false,
12472 indicatorpos : 'left',
12482 parentLabelAlign : function()
12485 while (parent.parent()) {
12486 parent = parent.parent();
12487 if (typeof(parent.labelAlign) !='undefined') {
12488 return parent.labelAlign;
12495 getAutoCreate : function()
12497 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12503 if(this.inputType != 'hidden'){
12504 cfg.cls = 'form-group' //input-group
12510 type : this.inputType,
12511 value : this.value,
12512 cls : 'form-control',
12513 placeholder : this.placeholder || '',
12514 autocomplete : this.autocomplete || 'new-password'
12516 if (this.inputType == 'file') {
12517 input.style = 'overflow:hidden'; // why not in CSS?
12520 if(this.capture.length){
12521 input.capture = this.capture;
12524 if(this.accept.length){
12525 input.accept = this.accept + "/*";
12529 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12532 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12533 input.maxLength = this.maxLength;
12536 if (this.disabled) {
12537 input.disabled=true;
12540 if (this.readOnly) {
12541 input.readonly=true;
12545 input.name = this.name;
12549 input.cls += ' input-' + this.size;
12553 ['xs','sm','md','lg'].map(function(size){
12554 if (settings[size]) {
12555 cfg.cls += ' col-' + size + '-' + settings[size];
12559 var inputblock = input;
12563 cls: 'glyphicon form-control-feedback'
12566 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12569 cls : 'has-feedback',
12577 if (this.before || this.after) {
12580 cls : 'input-group',
12584 if (this.before && typeof(this.before) == 'string') {
12586 inputblock.cn.push({
12588 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12592 if (this.before && typeof(this.before) == 'object') {
12593 this.before = Roo.factory(this.before);
12595 inputblock.cn.push({
12597 cls : 'roo-input-before input-group-prepend input-group-' +
12598 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12602 inputblock.cn.push(input);
12604 if (this.after && typeof(this.after) == 'string') {
12605 inputblock.cn.push({
12607 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12611 if (this.after && typeof(this.after) == 'object') {
12612 this.after = Roo.factory(this.after);
12614 inputblock.cn.push({
12616 cls : 'roo-input-after input-group-append input-group-' +
12617 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12621 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12622 inputblock.cls += ' has-feedback';
12623 inputblock.cn.push(feedback);
12628 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12629 tooltip : 'This field is required'
12631 if (this.allowBlank ) {
12632 indicator.style = this.allowBlank ? ' display:none' : '';
12634 if (align ==='left' && this.fieldLabel.length) {
12636 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12643 cls : 'control-label col-form-label',
12644 html : this.fieldLabel
12655 var labelCfg = cfg.cn[1];
12656 var contentCfg = cfg.cn[2];
12658 if(this.indicatorpos == 'right'){
12663 cls : 'control-label col-form-label',
12667 html : this.fieldLabel
12681 labelCfg = cfg.cn[0];
12682 contentCfg = cfg.cn[1];
12686 if(this.labelWidth > 12){
12687 labelCfg.style = "width: " + this.labelWidth + 'px';
12690 if(this.labelWidth < 13 && this.labelmd == 0){
12691 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12694 if(this.labellg > 0){
12695 labelCfg.cls += ' col-lg-' + this.labellg;
12696 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12699 if(this.labelmd > 0){
12700 labelCfg.cls += ' col-md-' + this.labelmd;
12701 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12704 if(this.labelsm > 0){
12705 labelCfg.cls += ' col-sm-' + this.labelsm;
12706 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12709 if(this.labelxs > 0){
12710 labelCfg.cls += ' col-xs-' + this.labelxs;
12711 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12715 } else if ( this.fieldLabel.length) {
12722 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12723 tooltip : 'This field is required',
12724 style : this.allowBlank ? ' display:none' : ''
12728 //cls : 'input-group-addon',
12729 html : this.fieldLabel
12737 if(this.indicatorpos == 'right'){
12742 //cls : 'input-group-addon',
12743 html : this.fieldLabel
12748 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12749 tooltip : 'This field is required',
12750 style : this.allowBlank ? ' display:none' : ''
12770 if (this.parentType === 'Navbar' && this.parent().bar) {
12771 cfg.cls += ' navbar-form';
12774 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12775 // on BS4 we do this only if not form
12776 cfg.cls += ' navbar-form';
12784 * return the real input element.
12786 inputEl: function ()
12788 return this.el.select('input.form-control',true).first();
12791 tooltipEl : function()
12793 return this.inputEl();
12796 indicatorEl : function()
12798 if (Roo.bootstrap.version == 4) {
12799 return false; // not enabled in v4 yet.
12802 var indicator = this.el.select('i.roo-required-indicator',true).first();
12812 setDisabled : function(v)
12814 var i = this.inputEl().dom;
12816 i.removeAttribute('disabled');
12820 i.setAttribute('disabled','true');
12822 initEvents : function()
12825 this.inputEl().on("keydown" , this.fireKey, this);
12826 this.inputEl().on("focus", this.onFocus, this);
12827 this.inputEl().on("blur", this.onBlur, this);
12829 this.inputEl().relayEvent('keyup', this);
12830 this.inputEl().relayEvent('paste', this);
12832 this.indicator = this.indicatorEl();
12834 if(this.indicator){
12835 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12838 // reference to original value for reset
12839 this.originalValue = this.getValue();
12840 //Roo.form.TextField.superclass.initEvents.call(this);
12841 if(this.validationEvent == 'keyup'){
12842 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12843 this.inputEl().on('keyup', this.filterValidation, this);
12845 else if(this.validationEvent !== false){
12846 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12849 if(this.selectOnFocus){
12850 this.on("focus", this.preFocus, this);
12853 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12854 this.inputEl().on("keypress", this.filterKeys, this);
12856 this.inputEl().relayEvent('keypress', this);
12859 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12860 this.el.on("click", this.autoSize, this);
12863 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12864 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12867 if (typeof(this.before) == 'object') {
12868 this.before.render(this.el.select('.roo-input-before',true).first());
12870 if (typeof(this.after) == 'object') {
12871 this.after.render(this.el.select('.roo-input-after',true).first());
12874 this.inputEl().on('change', this.onChange, this);
12877 filterValidation : function(e){
12878 if(!e.isNavKeyPress()){
12879 this.validationTask.delay(this.validationDelay);
12883 * Validates the field value
12884 * @return {Boolean} True if the value is valid, else false
12886 validate : function(){
12887 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12888 if(this.disabled || this.validateValue(this.getRawValue())){
12893 this.markInvalid();
12899 * Validates a value according to the field's validation rules and marks the field as invalid
12900 * if the validation fails
12901 * @param {Mixed} value The value to validate
12902 * @return {Boolean} True if the value is valid, else false
12904 validateValue : function(value)
12906 if(this.getVisibilityEl().hasClass('hidden')){
12910 if(value.length < 1) { // if it's blank
12911 if(this.allowBlank){
12917 if(value.length < this.minLength){
12920 if(value.length > this.maxLength){
12924 var vt = Roo.form.VTypes;
12925 if(!vt[this.vtype](value, this)){
12929 if(typeof this.validator == "function"){
12930 var msg = this.validator(value);
12934 if (typeof(msg) == 'string') {
12935 this.invalidText = msg;
12939 if(this.regex && !this.regex.test(value)){
12947 fireKey : function(e){
12948 //Roo.log('field ' + e.getKey());
12949 if(e.isNavKeyPress()){
12950 this.fireEvent("specialkey", this, e);
12953 focus : function (selectText){
12955 this.inputEl().focus();
12956 if(selectText === true){
12957 this.inputEl().dom.select();
12963 onFocus : function(){
12964 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12965 // this.el.addClass(this.focusClass);
12967 if(!this.hasFocus){
12968 this.hasFocus = true;
12969 this.startValue = this.getValue();
12970 this.fireEvent("focus", this);
12974 beforeBlur : Roo.emptyFn,
12978 onBlur : function(){
12980 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12981 //this.el.removeClass(this.focusClass);
12983 this.hasFocus = false;
12984 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12987 var v = this.getValue();
12988 if(String(v) !== String(this.startValue)){
12989 this.fireEvent('change', this, v, this.startValue);
12991 this.fireEvent("blur", this);
12994 onChange : function(e)
12996 var v = this.getValue();
12997 if(String(v) !== String(this.startValue)){
12998 this.fireEvent('change', this, v, this.startValue);
13004 * Resets the current field value to the originally loaded value and clears any validation messages
13006 reset : function(){
13007 this.setValue(this.originalValue);
13011 * Returns the name of the field
13012 * @return {Mixed} name The name field
13014 getName: function(){
13018 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13019 * @return {Mixed} value The field value
13021 getValue : function(){
13023 var v = this.inputEl().getValue();
13028 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13029 * @return {Mixed} value The field value
13031 getRawValue : function(){
13032 var v = this.inputEl().getValue();
13038 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13039 * @param {Mixed} value The value to set
13041 setRawValue : function(v){
13042 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13045 selectText : function(start, end){
13046 var v = this.getRawValue();
13048 start = start === undefined ? 0 : start;
13049 end = end === undefined ? v.length : end;
13050 var d = this.inputEl().dom;
13051 if(d.setSelectionRange){
13052 d.setSelectionRange(start, end);
13053 }else if(d.createTextRange){
13054 var range = d.createTextRange();
13055 range.moveStart("character", start);
13056 range.moveEnd("character", v.length-end);
13063 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13064 * @param {Mixed} value The value to set
13066 setValue : function(v){
13069 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13075 processValue : function(value){
13076 if(this.stripCharsRe){
13077 var newValue = value.replace(this.stripCharsRe, '');
13078 if(newValue !== value){
13079 this.setRawValue(newValue);
13086 preFocus : function(){
13088 if(this.selectOnFocus){
13089 this.inputEl().dom.select();
13092 filterKeys : function(e){
13093 var k = e.getKey();
13094 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13097 var c = e.getCharCode(), cc = String.fromCharCode(c);
13098 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13101 if(!this.maskRe.test(cc)){
13106 * Clear any invalid styles/messages for this field
13108 clearInvalid : function(){
13110 if(!this.el || this.preventMark){ // not rendered
13115 this.el.removeClass([this.invalidClass, 'is-invalid']);
13117 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13119 var feedback = this.el.select('.form-control-feedback', true).first();
13122 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13127 if(this.indicator){
13128 this.indicator.removeClass('visible');
13129 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13132 this.fireEvent('valid', this);
13136 * Mark this field as valid
13138 markValid : function()
13140 if(!this.el || this.preventMark){ // not rendered...
13144 this.el.removeClass([this.invalidClass, this.validClass]);
13145 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13147 var feedback = this.el.select('.form-control-feedback', true).first();
13150 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13153 if(this.indicator){
13154 this.indicator.removeClass('visible');
13155 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13163 if(this.allowBlank && !this.getRawValue().length){
13166 if (Roo.bootstrap.version == 3) {
13167 this.el.addClass(this.validClass);
13169 this.inputEl().addClass('is-valid');
13172 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13174 var feedback = this.el.select('.form-control-feedback', true).first();
13177 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13178 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13183 this.fireEvent('valid', this);
13187 * Mark this field as invalid
13188 * @param {String} msg The validation message
13190 markInvalid : function(msg)
13192 if(!this.el || this.preventMark){ // not rendered
13196 this.el.removeClass([this.invalidClass, this.validClass]);
13197 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13199 var feedback = this.el.select('.form-control-feedback', true).first();
13202 this.el.select('.form-control-feedback', true).first().removeClass(
13203 [this.invalidFeedbackClass, this.validFeedbackClass]);
13210 if(this.allowBlank && !this.getRawValue().length){
13214 if(this.indicator){
13215 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13216 this.indicator.addClass('visible');
13218 if (Roo.bootstrap.version == 3) {
13219 this.el.addClass(this.invalidClass);
13221 this.inputEl().addClass('is-invalid');
13226 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13228 var feedback = this.el.select('.form-control-feedback', true).first();
13231 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13233 if(this.getValue().length || this.forceFeedback){
13234 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13241 this.fireEvent('invalid', this, msg);
13244 SafariOnKeyDown : function(event)
13246 // this is a workaround for a password hang bug on chrome/ webkit.
13247 if (this.inputEl().dom.type != 'password') {
13251 var isSelectAll = false;
13253 if(this.inputEl().dom.selectionEnd > 0){
13254 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13256 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13257 event.preventDefault();
13262 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13264 event.preventDefault();
13265 // this is very hacky as keydown always get's upper case.
13267 var cc = String.fromCharCode(event.getCharCode());
13268 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13272 adjustWidth : function(tag, w){
13273 tag = tag.toLowerCase();
13274 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13275 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13276 if(tag == 'input'){
13279 if(tag == 'textarea'){
13282 }else if(Roo.isOpera){
13283 if(tag == 'input'){
13286 if(tag == 'textarea'){
13294 setFieldLabel : function(v)
13296 if(!this.rendered){
13300 if(this.indicatorEl()){
13301 var ar = this.el.select('label > span',true);
13303 if (ar.elements.length) {
13304 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13305 this.fieldLabel = v;
13309 var br = this.el.select('label',true);
13311 if(br.elements.length) {
13312 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13313 this.fieldLabel = v;
13317 Roo.log('Cannot Found any of label > span || label in input');
13321 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13322 this.fieldLabel = v;
13337 * @class Roo.bootstrap.TextArea
13338 * @extends Roo.bootstrap.Input
13339 * Bootstrap TextArea class
13340 * @cfg {Number} cols Specifies the visible width of a text area
13341 * @cfg {Number} rows Specifies the visible number of lines in a text area
13342 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13343 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13344 * @cfg {string} html text
13347 * Create a new TextArea
13348 * @param {Object} config The config object
13351 Roo.bootstrap.TextArea = function(config){
13352 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
13356 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
13366 getAutoCreate : function(){
13368 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13374 if(this.inputType != 'hidden'){
13375 cfg.cls = 'form-group' //input-group
13383 value : this.value || '',
13384 html: this.html || '',
13385 cls : 'form-control',
13386 placeholder : this.placeholder || ''
13390 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13391 input.maxLength = this.maxLength;
13395 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13399 input.cols = this.cols;
13402 if (this.readOnly) {
13403 input.readonly = true;
13407 input.name = this.name;
13411 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13415 ['xs','sm','md','lg'].map(function(size){
13416 if (settings[size]) {
13417 cfg.cls += ' col-' + size + '-' + settings[size];
13421 var inputblock = input;
13423 if(this.hasFeedback && !this.allowBlank){
13427 cls: 'glyphicon form-control-feedback'
13431 cls : 'has-feedback',
13440 if (this.before || this.after) {
13443 cls : 'input-group',
13447 inputblock.cn.push({
13449 cls : 'input-group-addon',
13454 inputblock.cn.push(input);
13456 if(this.hasFeedback && !this.allowBlank){
13457 inputblock.cls += ' has-feedback';
13458 inputblock.cn.push(feedback);
13462 inputblock.cn.push({
13464 cls : 'input-group-addon',
13471 if (align ==='left' && this.fieldLabel.length) {
13476 cls : 'control-label',
13477 html : this.fieldLabel
13488 if(this.labelWidth > 12){
13489 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13492 if(this.labelWidth < 13 && this.labelmd == 0){
13493 this.labelmd = this.labelWidth;
13496 if(this.labellg > 0){
13497 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13498 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13501 if(this.labelmd > 0){
13502 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13503 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13506 if(this.labelsm > 0){
13507 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13508 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13511 if(this.labelxs > 0){
13512 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13513 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13516 } else if ( this.fieldLabel.length) {
13521 //cls : 'input-group-addon',
13522 html : this.fieldLabel
13540 if (this.disabled) {
13541 input.disabled=true;
13548 * return the real textarea element.
13550 inputEl: function ()
13552 return this.el.select('textarea.form-control',true).first();
13556 * Clear any invalid styles/messages for this field
13558 clearInvalid : function()
13561 if(!this.el || this.preventMark){ // not rendered
13565 var label = this.el.select('label', true).first();
13566 var icon = this.el.select('i.fa-star', true).first();
13571 this.el.removeClass( this.validClass);
13572 this.inputEl().removeClass('is-invalid');
13574 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13576 var feedback = this.el.select('.form-control-feedback', true).first();
13579 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13584 this.fireEvent('valid', this);
13588 * Mark this field as valid
13590 markValid : function()
13592 if(!this.el || this.preventMark){ // not rendered
13596 this.el.removeClass([this.invalidClass, this.validClass]);
13597 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13599 var feedback = this.el.select('.form-control-feedback', true).first();
13602 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13605 if(this.disabled || this.allowBlank){
13609 var label = this.el.select('label', true).first();
13610 var icon = this.el.select('i.fa-star', true).first();
13615 if (Roo.bootstrap.version == 3) {
13616 this.el.addClass(this.validClass);
13618 this.inputEl().addClass('is-valid');
13622 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13624 var feedback = this.el.select('.form-control-feedback', true).first();
13627 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13628 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13633 this.fireEvent('valid', this);
13637 * Mark this field as invalid
13638 * @param {String} msg The validation message
13640 markInvalid : function(msg)
13642 if(!this.el || this.preventMark){ // not rendered
13646 this.el.removeClass([this.invalidClass, this.validClass]);
13647 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13649 var feedback = this.el.select('.form-control-feedback', true).first();
13652 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13655 if(this.disabled || this.allowBlank){
13659 var label = this.el.select('label', true).first();
13660 var icon = this.el.select('i.fa-star', true).first();
13662 if(!this.getValue().length && label && !icon){
13663 this.el.createChild({
13665 cls : 'text-danger fa fa-lg fa-star',
13666 tooltip : 'This field is required',
13667 style : 'margin-right:5px;'
13671 if (Roo.bootstrap.version == 3) {
13672 this.el.addClass(this.invalidClass);
13674 this.inputEl().addClass('is-invalid');
13677 // fixme ... this may be depricated need to test..
13678 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13680 var feedback = this.el.select('.form-control-feedback', true).first();
13683 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13685 if(this.getValue().length || this.forceFeedback){
13686 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13693 this.fireEvent('invalid', this, msg);
13701 * trigger field - base class for combo..
13706 * @class Roo.bootstrap.TriggerField
13707 * @extends Roo.bootstrap.Input
13708 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13709 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13710 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13711 * for which you can provide a custom implementation. For example:
13713 var trigger = new Roo.bootstrap.TriggerField();
13714 trigger.onTriggerClick = myTriggerFn;
13715 trigger.applyTo('my-field');
13718 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13719 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13720 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13721 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13722 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13725 * Create a new TriggerField.
13726 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13727 * to the base TextField)
13729 Roo.bootstrap.TriggerField = function(config){
13730 this.mimicing = false;
13731 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13734 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13736 * @cfg {String} triggerClass A CSS class to apply to the trigger
13739 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13744 * @cfg {Boolean} removable (true|false) special filter default false
13748 /** @cfg {Boolean} grow @hide */
13749 /** @cfg {Number} growMin @hide */
13750 /** @cfg {Number} growMax @hide */
13756 autoSize: Roo.emptyFn,
13760 deferHeight : true,
13763 actionMode : 'wrap',
13768 getAutoCreate : function(){
13770 var align = this.labelAlign || this.parentLabelAlign();
13775 cls: 'form-group' //input-group
13782 type : this.inputType,
13783 cls : 'form-control',
13784 autocomplete: 'new-password',
13785 placeholder : this.placeholder || ''
13789 input.name = this.name;
13792 input.cls += ' input-' + this.size;
13795 if (this.disabled) {
13796 input.disabled=true;
13799 var inputblock = input;
13801 if(this.hasFeedback && !this.allowBlank){
13805 cls: 'glyphicon form-control-feedback'
13808 if(this.removable && !this.editable ){
13810 cls : 'has-feedback',
13816 cls : 'roo-combo-removable-btn close'
13823 cls : 'has-feedback',
13832 if(this.removable && !this.editable ){
13834 cls : 'roo-removable',
13840 cls : 'roo-combo-removable-btn close'
13847 if (this.before || this.after) {
13850 cls : 'input-group',
13854 inputblock.cn.push({
13856 cls : 'input-group-addon input-group-prepend input-group-text',
13861 inputblock.cn.push(input);
13863 if(this.hasFeedback && !this.allowBlank){
13864 inputblock.cls += ' has-feedback';
13865 inputblock.cn.push(feedback);
13869 inputblock.cn.push({
13871 cls : 'input-group-addon input-group-append input-group-text',
13880 var ibwrap = inputblock;
13885 cls: 'roo-select2-choices',
13889 cls: 'roo-select2-search-field',
13901 cls: 'roo-select2-container input-group',
13906 cls: 'form-hidden-field'
13912 if(!this.multiple && this.showToggleBtn){
13918 if (this.caret != false) {
13921 cls: 'fa fa-' + this.caret
13928 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13930 Roo.bootstrap.version == 3 ? caret : '',
13933 cls: 'combobox-clear',
13947 combobox.cls += ' roo-select2-container-multi';
13951 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13952 tooltip : 'This field is required'
13954 if (Roo.bootstrap.version == 4) {
13957 style : 'display:none'
13962 if (align ==='left' && this.fieldLabel.length) {
13964 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13971 cls : 'control-label',
13972 html : this.fieldLabel
13984 var labelCfg = cfg.cn[1];
13985 var contentCfg = cfg.cn[2];
13987 if(this.indicatorpos == 'right'){
13992 cls : 'control-label',
13996 html : this.fieldLabel
14010 labelCfg = cfg.cn[0];
14011 contentCfg = cfg.cn[1];
14014 if(this.labelWidth > 12){
14015 labelCfg.style = "width: " + this.labelWidth + 'px';
14018 if(this.labelWidth < 13 && this.labelmd == 0){
14019 this.labelmd = this.labelWidth;
14022 if(this.labellg > 0){
14023 labelCfg.cls += ' col-lg-' + this.labellg;
14024 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14027 if(this.labelmd > 0){
14028 labelCfg.cls += ' col-md-' + this.labelmd;
14029 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14032 if(this.labelsm > 0){
14033 labelCfg.cls += ' col-sm-' + this.labelsm;
14034 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14037 if(this.labelxs > 0){
14038 labelCfg.cls += ' col-xs-' + this.labelxs;
14039 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14042 } else if ( this.fieldLabel.length) {
14043 // Roo.log(" label");
14048 //cls : 'input-group-addon',
14049 html : this.fieldLabel
14057 if(this.indicatorpos == 'right'){
14065 html : this.fieldLabel
14079 // Roo.log(" no label && no align");
14086 ['xs','sm','md','lg'].map(function(size){
14087 if (settings[size]) {
14088 cfg.cls += ' col-' + size + '-' + settings[size];
14099 onResize : function(w, h){
14100 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
14101 // if(typeof w == 'number'){
14102 // var x = w - this.trigger.getWidth();
14103 // this.inputEl().setWidth(this.adjustWidth('input', x));
14104 // this.trigger.setStyle('left', x+'px');
14109 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14112 getResizeEl : function(){
14113 return this.inputEl();
14117 getPositionEl : function(){
14118 return this.inputEl();
14122 alignErrorIcon : function(){
14123 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14127 initEvents : function(){
14131 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
14132 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14133 if(!this.multiple && this.showToggleBtn){
14134 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14135 if(this.hideTrigger){
14136 this.trigger.setDisplayed(false);
14138 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14142 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14145 if(this.removable && !this.editable && !this.tickable){
14146 var close = this.closeTriggerEl();
14149 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14150 close.on('click', this.removeBtnClick, this, close);
14154 //this.trigger.addClassOnOver('x-form-trigger-over');
14155 //this.trigger.addClassOnClick('x-form-trigger-click');
14158 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14162 closeTriggerEl : function()
14164 var close = this.el.select('.roo-combo-removable-btn', true).first();
14165 return close ? close : false;
14168 removeBtnClick : function(e, h, el)
14170 e.preventDefault();
14172 if(this.fireEvent("remove", this) !== false){
14174 this.fireEvent("afterremove", this)
14178 createList : function()
14180 this.list = Roo.get(document.body).createChild({
14181 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14182 cls: 'typeahead typeahead-long dropdown-menu shadow',
14183 style: 'display:none'
14186 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14191 initTrigger : function(){
14196 onDestroy : function(){
14198 this.trigger.removeAllListeners();
14199 // this.trigger.remove();
14202 // this.wrap.remove();
14204 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
14208 onFocus : function(){
14209 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
14211 if(!this.mimicing){
14212 this.wrap.addClass('x-trigger-wrap-focus');
14213 this.mimicing = true;
14214 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14215 if(this.monitorTab){
14216 this.el.on("keydown", this.checkTab, this);
14223 checkTab : function(e){
14224 if(e.getKey() == e.TAB){
14225 this.triggerBlur();
14230 onBlur : function(){
14235 mimicBlur : function(e, t){
14237 if(!this.wrap.contains(t) && this.validateBlur()){
14238 this.triggerBlur();
14244 triggerBlur : function(){
14245 this.mimicing = false;
14246 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14247 if(this.monitorTab){
14248 this.el.un("keydown", this.checkTab, this);
14250 //this.wrap.removeClass('x-trigger-wrap-focus');
14251 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
14255 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14256 validateBlur : function(e, t){
14261 onDisable : function(){
14262 this.inputEl().dom.disabled = true;
14263 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
14265 // this.wrap.addClass('x-item-disabled');
14270 onEnable : function(){
14271 this.inputEl().dom.disabled = false;
14272 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
14274 // this.el.removeClass('x-item-disabled');
14279 onShow : function(){
14280 var ae = this.getActionEl();
14283 ae.dom.style.display = '';
14284 ae.dom.style.visibility = 'visible';
14290 onHide : function(){
14291 var ae = this.getActionEl();
14292 ae.dom.style.display = 'none';
14296 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14297 * by an implementing function.
14299 * @param {EventObject} e
14301 onTriggerClick : Roo.emptyFn
14309 * @class Roo.bootstrap.CardUploader
14310 * @extends Roo.bootstrap.Button
14311 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14312 * @cfg {Number} errorTimeout default 3000
14313 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14314 * @cfg {Array} html The button text.
14318 * Create a new CardUploader
14319 * @param {Object} config The config object
14322 Roo.bootstrap.CardUploader = function(config){
14326 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
14329 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14337 * When a image is clicked on - and needs to display a slideshow or similar..
14338 * @param {Roo.bootstrap.Card} this
14339 * @param {Object} The image information data
14345 * When a the download link is clicked
14346 * @param {Roo.bootstrap.Card} this
14347 * @param {Object} The image information data contains
14354 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
14357 errorTimeout : 3000,
14361 fileCollection : false,
14364 getAutoCreate : function()
14368 cls :'form-group' ,
14373 //cls : 'input-group-addon',
14374 html : this.fieldLabel
14382 value : this.value,
14383 cls : 'd-none form-control'
14388 multiple : 'multiple',
14390 cls : 'd-none roo-card-upload-selector'
14394 cls : 'roo-card-uploader-button-container w-100 mb-2'
14397 cls : 'card-columns roo-card-uploader-container'
14407 getChildContainer : function() /// what children are added to.
14409 return this.containerEl;
14412 getButtonContainer : function() /// what children are added to.
14414 return this.el.select(".roo-card-uploader-button-container").first();
14417 initEvents : function()
14420 Roo.bootstrap.Input.prototype.initEvents.call(this);
14424 xns: Roo.bootstrap,
14427 container_method : 'getButtonContainer' ,
14428 html : this.html, // fix changable?
14431 'click' : function(btn, e) {
14440 this.urlAPI = (window.createObjectURL && window) ||
14441 (window.URL && URL.revokeObjectURL && URL) ||
14442 (window.webkitURL && webkitURL);
14447 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14449 this.selectorEl.on('change', this.onFileSelected, this);
14452 this.images.forEach(function(img) {
14455 this.images = false;
14457 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14463 onClick : function(e)
14465 e.preventDefault();
14467 this.selectorEl.dom.click();
14471 onFileSelected : function(e)
14473 e.preventDefault();
14475 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14479 Roo.each(this.selectorEl.dom.files, function(file){
14480 this.addFile(file);
14489 addFile : function(file)
14492 if(typeof(file) === 'string'){
14493 throw "Add file by name?"; // should not happen
14497 if(!file || !this.urlAPI){
14507 var url = _this.urlAPI.createObjectURL( file);
14510 id : Roo.bootstrap.CardUploader.ID--,
14511 is_uploaded : false,
14515 mimetype : file.type,
14523 * addCard - add an Attachment to the uploader
14524 * @param data - the data about the image to upload
14528 title : "Title of file",
14529 is_uploaded : false,
14530 src : "http://.....",
14531 srcfile : { the File upload object },
14532 mimetype : file.type,
14535 .. any other data...
14541 addCard : function (data)
14543 // hidden input element?
14544 // if the file is not an image...
14545 //then we need to use something other that and header_image
14550 xns : Roo.bootstrap,
14551 xtype : 'CardFooter',
14554 xns : Roo.bootstrap,
14560 xns : Roo.bootstrap,
14562 html : String.format("<small>{0}</small>", data.title),
14563 cls : 'col-10 text-left',
14568 click : function() {
14570 t.fireEvent( "download", t, data );
14576 xns : Roo.bootstrap,
14578 style: 'max-height: 28px; ',
14584 click : function() {
14585 t.removeCard(data.id)
14597 var cn = this.addxtype(
14600 xns : Roo.bootstrap,
14603 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14604 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14605 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14610 initEvents : function() {
14611 Roo.bootstrap.Card.prototype.initEvents.call(this);
14613 this.imgEl = this.el.select('.card-img-top').first();
14615 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14616 this.imgEl.set({ 'pointer' : 'cursor' });
14619 this.getCardFooter().addClass('p-1');
14626 // dont' really need ot update items.
14627 // this.items.push(cn);
14628 this.fileCollection.add(cn);
14630 if (!data.srcfile) {
14631 this.updateInput();
14636 var reader = new FileReader();
14637 reader.addEventListener("load", function() {
14638 data.srcdata = reader.result;
14641 reader.readAsDataURL(data.srcfile);
14646 removeCard : function(id)
14649 var card = this.fileCollection.get(id);
14650 card.data.is_deleted = 1;
14651 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14652 //this.fileCollection.remove(card);
14653 //this.items = this.items.filter(function(e) { return e != card });
14654 // dont' really need ot update items.
14655 card.el.dom.parentNode.removeChild(card.el.dom);
14656 this.updateInput();
14662 this.fileCollection.each(function(card) {
14663 if (card.el.dom && card.el.dom.parentNode) {
14664 card.el.dom.parentNode.removeChild(card.el.dom);
14667 this.fileCollection.clear();
14668 this.updateInput();
14671 updateInput : function()
14674 this.fileCollection.each(function(e) {
14678 this.inputEl().dom.value = JSON.stringify(data);
14688 Roo.bootstrap.CardUploader.ID = -1;/*
14690 * Ext JS Library 1.1.1
14691 * Copyright(c) 2006-2007, Ext JS, LLC.
14693 * Originally Released Under LGPL - original licence link has changed is not relivant.
14696 * <script type="text/javascript">
14701 * @class Roo.data.SortTypes
14703 * Defines the default sorting (casting?) comparison functions used when sorting data.
14705 Roo.data.SortTypes = {
14707 * Default sort that does nothing
14708 * @param {Mixed} s The value being converted
14709 * @return {Mixed} The comparison value
14711 none : function(s){
14716 * The regular expression used to strip tags
14720 stripTagsRE : /<\/?[^>]+>/gi,
14723 * Strips all HTML tags to sort on text only
14724 * @param {Mixed} s The value being converted
14725 * @return {String} The comparison value
14727 asText : function(s){
14728 return String(s).replace(this.stripTagsRE, "");
14732 * Strips all HTML tags to sort on text only - Case insensitive
14733 * @param {Mixed} s The value being converted
14734 * @return {String} The comparison value
14736 asUCText : function(s){
14737 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14741 * Case insensitive string
14742 * @param {Mixed} s The value being converted
14743 * @return {String} The comparison value
14745 asUCString : function(s) {
14746 return String(s).toUpperCase();
14751 * @param {Mixed} s The value being converted
14752 * @return {Number} The comparison value
14754 asDate : function(s) {
14758 if(s instanceof Date){
14759 return s.getTime();
14761 return Date.parse(String(s));
14766 * @param {Mixed} s The value being converted
14767 * @return {Float} The comparison value
14769 asFloat : function(s) {
14770 var val = parseFloat(String(s).replace(/,/g, ""));
14779 * @param {Mixed} s The value being converted
14780 * @return {Number} The comparison value
14782 asInt : function(s) {
14783 var val = parseInt(String(s).replace(/,/g, ""));
14791 * Ext JS Library 1.1.1
14792 * Copyright(c) 2006-2007, Ext JS, LLC.
14794 * Originally Released Under LGPL - original licence link has changed is not relivant.
14797 * <script type="text/javascript">
14801 * @class Roo.data.Record
14802 * Instances of this class encapsulate both record <em>definition</em> information, and record
14803 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14804 * to access Records cached in an {@link Roo.data.Store} object.<br>
14806 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14807 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14810 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14812 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14813 * {@link #create}. The parameters are the same.
14814 * @param {Array} data An associative Array of data values keyed by the field name.
14815 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14816 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14817 * not specified an integer id is generated.
14819 Roo.data.Record = function(data, id){
14820 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14825 * Generate a constructor for a specific record layout.
14826 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14827 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14828 * Each field definition object may contain the following properties: <ul>
14829 * <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,
14830 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14831 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14832 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14833 * is being used, then this is a string containing the javascript expression to reference the data relative to
14834 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14835 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14836 * this may be omitted.</p></li>
14837 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14838 * <ul><li>auto (Default, implies no conversion)</li>
14843 * <li>date</li></ul></p></li>
14844 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14845 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14846 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14847 * by the Reader into an object that will be stored in the Record. It is passed the
14848 * following parameters:<ul>
14849 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14851 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14853 * <br>usage:<br><pre><code>
14854 var TopicRecord = Roo.data.Record.create(
14855 {name: 'title', mapping: 'topic_title'},
14856 {name: 'author', mapping: 'username'},
14857 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14858 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14859 {name: 'lastPoster', mapping: 'user2'},
14860 {name: 'excerpt', mapping: 'post_text'}
14863 var myNewRecord = new TopicRecord({
14864 title: 'Do my job please',
14867 lastPost: new Date(),
14868 lastPoster: 'Animal',
14869 excerpt: 'No way dude!'
14871 myStore.add(myNewRecord);
14876 Roo.data.Record.create = function(o){
14877 var f = function(){
14878 f.superclass.constructor.apply(this, arguments);
14880 Roo.extend(f, Roo.data.Record);
14881 var p = f.prototype;
14882 p.fields = new Roo.util.MixedCollection(false, function(field){
14885 for(var i = 0, len = o.length; i < len; i++){
14886 p.fields.add(new Roo.data.Field(o[i]));
14888 f.getField = function(name){
14889 return p.fields.get(name);
14894 Roo.data.Record.AUTO_ID = 1000;
14895 Roo.data.Record.EDIT = 'edit';
14896 Roo.data.Record.REJECT = 'reject';
14897 Roo.data.Record.COMMIT = 'commit';
14899 Roo.data.Record.prototype = {
14901 * Readonly flag - true if this record has been modified.
14910 join : function(store){
14911 this.store = store;
14915 * Set the named field to the specified value.
14916 * @param {String} name The name of the field to set.
14917 * @param {Object} value The value to set the field to.
14919 set : function(name, value){
14920 if(this.data[name] == value){
14924 if(!this.modified){
14925 this.modified = {};
14927 if(typeof this.modified[name] == 'undefined'){
14928 this.modified[name] = this.data[name];
14930 this.data[name] = value;
14931 if(!this.editing && this.store){
14932 this.store.afterEdit(this);
14937 * Get the value of the named field.
14938 * @param {String} name The name of the field to get the value of.
14939 * @return {Object} The value of the field.
14941 get : function(name){
14942 return this.data[name];
14946 beginEdit : function(){
14947 this.editing = true;
14948 this.modified = {};
14952 cancelEdit : function(){
14953 this.editing = false;
14954 delete this.modified;
14958 endEdit : function(){
14959 this.editing = false;
14960 if(this.dirty && this.store){
14961 this.store.afterEdit(this);
14966 * Usually called by the {@link Roo.data.Store} which owns the Record.
14967 * Rejects all changes made to the Record since either creation, or the last commit operation.
14968 * Modified fields are reverted to their original values.
14970 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14971 * of reject operations.
14973 reject : function(){
14974 var m = this.modified;
14976 if(typeof m[n] != "function"){
14977 this.data[n] = m[n];
14980 this.dirty = false;
14981 delete this.modified;
14982 this.editing = false;
14984 this.store.afterReject(this);
14989 * Usually called by the {@link Roo.data.Store} which owns the Record.
14990 * Commits all changes made to the Record since either creation, or the last commit operation.
14992 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14993 * of commit operations.
14995 commit : function(){
14996 this.dirty = false;
14997 delete this.modified;
14998 this.editing = false;
15000 this.store.afterCommit(this);
15005 hasError : function(){
15006 return this.error != null;
15010 clearError : function(){
15015 * Creates a copy of this record.
15016 * @param {String} id (optional) A new record id if you don't want to use this record's id
15019 copy : function(newId) {
15020 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15024 * Ext JS Library 1.1.1
15025 * Copyright(c) 2006-2007, Ext JS, LLC.
15027 * Originally Released Under LGPL - original licence link has changed is not relivant.
15030 * <script type="text/javascript">
15036 * @class Roo.data.Store
15037 * @extends Roo.util.Observable
15038 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15039 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15041 * 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
15042 * has no knowledge of the format of the data returned by the Proxy.<br>
15044 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15045 * instances from the data object. These records are cached and made available through accessor functions.
15047 * Creates a new Store.
15048 * @param {Object} config A config object containing the objects needed for the Store to access data,
15049 * and read the data into Records.
15051 Roo.data.Store = function(config){
15052 this.data = new Roo.util.MixedCollection(false);
15053 this.data.getKey = function(o){
15056 this.baseParams = {};
15058 this.paramNames = {
15063 "multisort" : "_multisort"
15066 if(config && config.data){
15067 this.inlineData = config.data;
15068 delete config.data;
15071 Roo.apply(this, config);
15073 if(this.reader){ // reader passed
15074 this.reader = Roo.factory(this.reader, Roo.data);
15075 this.reader.xmodule = this.xmodule || false;
15076 if(!this.recordType){
15077 this.recordType = this.reader.recordType;
15079 if(this.reader.onMetaChange){
15080 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15084 if(this.recordType){
15085 this.fields = this.recordType.prototype.fields;
15087 this.modified = [];
15091 * @event datachanged
15092 * Fires when the data cache has changed, and a widget which is using this Store
15093 * as a Record cache should refresh its view.
15094 * @param {Store} this
15096 datachanged : true,
15098 * @event metachange
15099 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15100 * @param {Store} this
15101 * @param {Object} meta The JSON metadata
15106 * Fires when Records have been added to the Store
15107 * @param {Store} this
15108 * @param {Roo.data.Record[]} records The array of Records added
15109 * @param {Number} index The index at which the record(s) were added
15114 * Fires when a Record has been removed from the Store
15115 * @param {Store} this
15116 * @param {Roo.data.Record} record The Record that was removed
15117 * @param {Number} index The index at which the record was removed
15122 * Fires when a Record has been updated
15123 * @param {Store} this
15124 * @param {Roo.data.Record} record The Record that was updated
15125 * @param {String} operation The update operation being performed. Value may be one of:
15127 Roo.data.Record.EDIT
15128 Roo.data.Record.REJECT
15129 Roo.data.Record.COMMIT
15135 * Fires when the data cache has been cleared.
15136 * @param {Store} this
15140 * @event beforeload
15141 * Fires before a request is made for a new data object. If the beforeload handler returns false
15142 * the load action will be canceled.
15143 * @param {Store} this
15144 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15148 * @event beforeloadadd
15149 * Fires after a new set of Records has been loaded.
15150 * @param {Store} this
15151 * @param {Roo.data.Record[]} records The Records that were loaded
15152 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15154 beforeloadadd : true,
15157 * Fires after a new set of Records has been loaded, before they are added to the store.
15158 * @param {Store} this
15159 * @param {Roo.data.Record[]} records The Records that were loaded
15160 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15161 * @params {Object} return from reader
15165 * @event loadexception
15166 * Fires if an exception occurs in the Proxy during loading.
15167 * Called with the signature of the Proxy's "loadexception" event.
15168 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15171 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15172 * @param {Object} load options
15173 * @param {Object} jsonData from your request (normally this contains the Exception)
15175 loadexception : true
15179 this.proxy = Roo.factory(this.proxy, Roo.data);
15180 this.proxy.xmodule = this.xmodule || false;
15181 this.relayEvents(this.proxy, ["loadexception"]);
15183 this.sortToggle = {};
15184 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15186 Roo.data.Store.superclass.constructor.call(this);
15188 if(this.inlineData){
15189 this.loadData(this.inlineData);
15190 delete this.inlineData;
15194 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15196 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15197 * without a remote query - used by combo/forms at present.
15201 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15204 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15207 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15208 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15211 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15212 * on any HTTP request
15215 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15218 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15222 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15223 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15225 remoteSort : false,
15228 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15229 * loaded or when a record is removed. (defaults to false).
15231 pruneModifiedRecords : false,
15234 lastOptions : null,
15237 * Add Records to the Store and fires the add event.
15238 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15240 add : function(records){
15241 records = [].concat(records);
15242 for(var i = 0, len = records.length; i < len; i++){
15243 records[i].join(this);
15245 var index = this.data.length;
15246 this.data.addAll(records);
15247 this.fireEvent("add", this, records, index);
15251 * Remove a Record from the Store and fires the remove event.
15252 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15254 remove : function(record){
15255 var index = this.data.indexOf(record);
15256 this.data.removeAt(index);
15258 if(this.pruneModifiedRecords){
15259 this.modified.remove(record);
15261 this.fireEvent("remove", this, record, index);
15265 * Remove all Records from the Store and fires the clear event.
15267 removeAll : function(){
15269 if(this.pruneModifiedRecords){
15270 this.modified = [];
15272 this.fireEvent("clear", this);
15276 * Inserts Records to the Store at the given index and fires the add event.
15277 * @param {Number} index The start index at which to insert the passed Records.
15278 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15280 insert : function(index, records){
15281 records = [].concat(records);
15282 for(var i = 0, len = records.length; i < len; i++){
15283 this.data.insert(index, records[i]);
15284 records[i].join(this);
15286 this.fireEvent("add", this, records, index);
15290 * Get the index within the cache of the passed Record.
15291 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15292 * @return {Number} The index of the passed Record. Returns -1 if not found.
15294 indexOf : function(record){
15295 return this.data.indexOf(record);
15299 * Get the index within the cache of the Record with the passed id.
15300 * @param {String} id The id of the Record to find.
15301 * @return {Number} The index of the Record. Returns -1 if not found.
15303 indexOfId : function(id){
15304 return this.data.indexOfKey(id);
15308 * Get the Record with the specified id.
15309 * @param {String} id The id of the Record to find.
15310 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15312 getById : function(id){
15313 return this.data.key(id);
15317 * Get the Record at the specified index.
15318 * @param {Number} index The index of the Record to find.
15319 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15321 getAt : function(index){
15322 return this.data.itemAt(index);
15326 * Returns a range of Records between specified indices.
15327 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15328 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15329 * @return {Roo.data.Record[]} An array of Records
15331 getRange : function(start, end){
15332 return this.data.getRange(start, end);
15336 storeOptions : function(o){
15337 o = Roo.apply({}, o);
15340 this.lastOptions = o;
15344 * Loads the Record cache from the configured Proxy using the configured Reader.
15346 * If using remote paging, then the first load call must specify the <em>start</em>
15347 * and <em>limit</em> properties in the options.params property to establish the initial
15348 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15350 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15351 * and this call will return before the new data has been loaded. Perform any post-processing
15352 * in a callback function, or in a "load" event handler.</strong>
15354 * @param {Object} options An object containing properties which control loading options:<ul>
15355 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15356 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15357 * passed the following arguments:<ul>
15358 * <li>r : Roo.data.Record[]</li>
15359 * <li>options: Options object from the load call</li>
15360 * <li>success: Boolean success indicator</li></ul></li>
15361 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15362 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15365 load : function(options){
15366 options = options || {};
15367 if(this.fireEvent("beforeload", this, options) !== false){
15368 this.storeOptions(options);
15369 var p = Roo.apply(options.params || {}, this.baseParams);
15370 // if meta was not loaded from remote source.. try requesting it.
15371 if (!this.reader.metaFromRemote) {
15372 p._requestMeta = 1;
15374 if(this.sortInfo && this.remoteSort){
15375 var pn = this.paramNames;
15376 p[pn["sort"]] = this.sortInfo.field;
15377 p[pn["dir"]] = this.sortInfo.direction;
15379 if (this.multiSort) {
15380 var pn = this.paramNames;
15381 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15384 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15389 * Reloads the Record cache from the configured Proxy using the configured Reader and
15390 * the options from the last load operation performed.
15391 * @param {Object} options (optional) An object containing properties which may override the options
15392 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15393 * the most recently used options are reused).
15395 reload : function(options){
15396 this.load(Roo.applyIf(options||{}, this.lastOptions));
15400 // Called as a callback by the Reader during a load operation.
15401 loadRecords : function(o, options, success){
15402 if(!o || success === false){
15403 if(success !== false){
15404 this.fireEvent("load", this, [], options, o);
15406 if(options.callback){
15407 options.callback.call(options.scope || this, [], options, false);
15411 // if data returned failure - throw an exception.
15412 if (o.success === false) {
15413 // show a message if no listener is registered.
15414 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15415 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15417 // loadmask wil be hooked into this..
15418 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15421 var r = o.records, t = o.totalRecords || r.length;
15423 this.fireEvent("beforeloadadd", this, r, options, o);
15425 if(!options || options.add !== true){
15426 if(this.pruneModifiedRecords){
15427 this.modified = [];
15429 for(var i = 0, len = r.length; i < len; i++){
15433 this.data = this.snapshot;
15434 delete this.snapshot;
15437 this.data.addAll(r);
15438 this.totalLength = t;
15440 this.fireEvent("datachanged", this);
15442 this.totalLength = Math.max(t, this.data.length+r.length);
15446 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15448 var e = new Roo.data.Record({});
15450 e.set(this.parent.displayField, this.parent.emptyTitle);
15451 e.set(this.parent.valueField, '');
15456 this.fireEvent("load", this, r, options, o);
15457 if(options.callback){
15458 options.callback.call(options.scope || this, r, options, true);
15464 * Loads data from a passed data block. A Reader which understands the format of the data
15465 * must have been configured in the constructor.
15466 * @param {Object} data The data block from which to read the Records. The format of the data expected
15467 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15468 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15470 loadData : function(o, append){
15471 var r = this.reader.readRecords(o);
15472 this.loadRecords(r, {add: append}, true);
15476 * using 'cn' the nested child reader read the child array into it's child stores.
15477 * @param {Object} rec The record with a 'children array
15479 loadDataFromChildren : function(rec)
15481 this.loadData(this.reader.toLoadData(rec));
15486 * Gets the number of cached records.
15488 * <em>If using paging, this may not be the total size of the dataset. If the data object
15489 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15490 * the data set size</em>
15492 getCount : function(){
15493 return this.data.length || 0;
15497 * Gets the total number of records in the dataset as returned by the server.
15499 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15500 * the dataset size</em>
15502 getTotalCount : function(){
15503 return this.totalLength || 0;
15507 * Returns the sort state of the Store as an object with two properties:
15509 field {String} The name of the field by which the Records are sorted
15510 direction {String} The sort order, "ASC" or "DESC"
15513 getSortState : function(){
15514 return this.sortInfo;
15518 applySort : function(){
15519 if(this.sortInfo && !this.remoteSort){
15520 var s = this.sortInfo, f = s.field;
15521 var st = this.fields.get(f).sortType;
15522 var fn = function(r1, r2){
15523 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15524 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15526 this.data.sort(s.direction, fn);
15527 if(this.snapshot && this.snapshot != this.data){
15528 this.snapshot.sort(s.direction, fn);
15534 * Sets the default sort column and order to be used by the next load operation.
15535 * @param {String} fieldName The name of the field to sort by.
15536 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15538 setDefaultSort : function(field, dir){
15539 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15543 * Sort the Records.
15544 * If remote sorting is used, the sort is performed on the server, and the cache is
15545 * reloaded. If local sorting is used, the cache is sorted internally.
15546 * @param {String} fieldName The name of the field to sort by.
15547 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15549 sort : function(fieldName, dir){
15550 var f = this.fields.get(fieldName);
15552 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15554 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15555 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15560 this.sortToggle[f.name] = dir;
15561 this.sortInfo = {field: f.name, direction: dir};
15562 if(!this.remoteSort){
15564 this.fireEvent("datachanged", this);
15566 this.load(this.lastOptions);
15571 * Calls the specified function for each of the Records in the cache.
15572 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15573 * Returning <em>false</em> aborts and exits the iteration.
15574 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15576 each : function(fn, scope){
15577 this.data.each(fn, scope);
15581 * Gets all records modified since the last commit. Modified records are persisted across load operations
15582 * (e.g., during paging).
15583 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15585 getModifiedRecords : function(){
15586 return this.modified;
15590 createFilterFn : function(property, value, anyMatch){
15591 if(!value.exec){ // not a regex
15592 value = String(value);
15593 if(value.length == 0){
15596 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15598 return function(r){
15599 return value.test(r.data[property]);
15604 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15605 * @param {String} property A field on your records
15606 * @param {Number} start The record index to start at (defaults to 0)
15607 * @param {Number} end The last record index to include (defaults to length - 1)
15608 * @return {Number} The sum
15610 sum : function(property, start, end){
15611 var rs = this.data.items, v = 0;
15612 start = start || 0;
15613 end = (end || end === 0) ? end : rs.length-1;
15615 for(var i = start; i <= end; i++){
15616 v += (rs[i].data[property] || 0);
15622 * Filter the records by a specified property.
15623 * @param {String} field A field on your records
15624 * @param {String/RegExp} value Either a string that the field
15625 * should start with or a RegExp to test against the field
15626 * @param {Boolean} anyMatch True to match any part not just the beginning
15628 filter : function(property, value, anyMatch){
15629 var fn = this.createFilterFn(property, value, anyMatch);
15630 return fn ? this.filterBy(fn) : this.clearFilter();
15634 * Filter by a function. The specified function will be called with each
15635 * record in this data source. If the function returns true the record is included,
15636 * otherwise it is filtered.
15637 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15638 * @param {Object} scope (optional) The scope of the function (defaults to this)
15640 filterBy : function(fn, scope){
15641 this.snapshot = this.snapshot || this.data;
15642 this.data = this.queryBy(fn, scope||this);
15643 this.fireEvent("datachanged", this);
15647 * Query the records by a specified property.
15648 * @param {String} field A field on your records
15649 * @param {String/RegExp} value Either a string that the field
15650 * should start with or a RegExp to test against the field
15651 * @param {Boolean} anyMatch True to match any part not just the beginning
15652 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15654 query : function(property, value, anyMatch){
15655 var fn = this.createFilterFn(property, value, anyMatch);
15656 return fn ? this.queryBy(fn) : this.data.clone();
15660 * Query by a function. The specified function will be called with each
15661 * record in this data source. If the function returns true the record is included
15663 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15664 * @param {Object} scope (optional) The scope of the function (defaults to this)
15665 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15667 queryBy : function(fn, scope){
15668 var data = this.snapshot || this.data;
15669 return data.filterBy(fn, scope||this);
15673 * Collects unique values for a particular dataIndex from this store.
15674 * @param {String} dataIndex The property to collect
15675 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15676 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15677 * @return {Array} An array of the unique values
15679 collect : function(dataIndex, allowNull, bypassFilter){
15680 var d = (bypassFilter === true && this.snapshot) ?
15681 this.snapshot.items : this.data.items;
15682 var v, sv, r = [], l = {};
15683 for(var i = 0, len = d.length; i < len; i++){
15684 v = d[i].data[dataIndex];
15686 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15695 * Revert to a view of the Record cache with no filtering applied.
15696 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15698 clearFilter : function(suppressEvent){
15699 if(this.snapshot && this.snapshot != this.data){
15700 this.data = this.snapshot;
15701 delete this.snapshot;
15702 if(suppressEvent !== true){
15703 this.fireEvent("datachanged", this);
15709 afterEdit : function(record){
15710 if(this.modified.indexOf(record) == -1){
15711 this.modified.push(record);
15713 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15717 afterReject : function(record){
15718 this.modified.remove(record);
15719 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15723 afterCommit : function(record){
15724 this.modified.remove(record);
15725 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15729 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15730 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15732 commitChanges : function(){
15733 var m = this.modified.slice(0);
15734 this.modified = [];
15735 for(var i = 0, len = m.length; i < len; i++){
15741 * Cancel outstanding changes on all changed records.
15743 rejectChanges : function(){
15744 var m = this.modified.slice(0);
15745 this.modified = [];
15746 for(var i = 0, len = m.length; i < len; i++){
15751 onMetaChange : function(meta, rtype, o){
15752 this.recordType = rtype;
15753 this.fields = rtype.prototype.fields;
15754 delete this.snapshot;
15755 this.sortInfo = meta.sortInfo || this.sortInfo;
15756 this.modified = [];
15757 this.fireEvent('metachange', this, this.reader.meta);
15760 moveIndex : function(data, type)
15762 var index = this.indexOf(data);
15764 var newIndex = index + type;
15768 this.insert(newIndex, data);
15773 * Ext JS Library 1.1.1
15774 * Copyright(c) 2006-2007, Ext JS, LLC.
15776 * Originally Released Under LGPL - original licence link has changed is not relivant.
15779 * <script type="text/javascript">
15783 * @class Roo.data.SimpleStore
15784 * @extends Roo.data.Store
15785 * Small helper class to make creating Stores from Array data easier.
15786 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15787 * @cfg {Array} fields An array of field definition objects, or field name strings.
15788 * @cfg {Object} an existing reader (eg. copied from another store)
15789 * @cfg {Array} data The multi-dimensional array of data
15790 * @cfg {Roo.data.DataProxy} proxy [not-required]
15791 * @cfg {Roo.data.Reader} reader [not-required]
15793 * @param {Object} config
15795 Roo.data.SimpleStore = function(config)
15797 Roo.data.SimpleStore.superclass.constructor.call(this, {
15799 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15802 Roo.data.Record.create(config.fields)
15804 proxy : new Roo.data.MemoryProxy(config.data)
15808 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15810 * Ext JS Library 1.1.1
15811 * Copyright(c) 2006-2007, Ext JS, LLC.
15813 * Originally Released Under LGPL - original licence link has changed is not relivant.
15816 * <script type="text/javascript">
15821 * @extends Roo.data.Store
15822 * @class Roo.data.JsonStore
15823 * Small helper class to make creating Stores for JSON data easier. <br/>
15825 var store = new Roo.data.JsonStore({
15826 url: 'get-images.php',
15828 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15831 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15832 * JsonReader and HttpProxy (unless inline data is provided).</b>
15833 * @cfg {Array} fields An array of field definition objects, or field name strings.
15835 * @param {Object} config
15837 Roo.data.JsonStore = function(c){
15838 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15839 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15840 reader: new Roo.data.JsonReader(c, c.fields)
15843 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15845 * Ext JS Library 1.1.1
15846 * Copyright(c) 2006-2007, Ext JS, LLC.
15848 * Originally Released Under LGPL - original licence link has changed is not relivant.
15851 * <script type="text/javascript">
15855 Roo.data.Field = function(config){
15856 if(typeof config == "string"){
15857 config = {name: config};
15859 Roo.apply(this, config);
15862 this.type = "auto";
15865 var st = Roo.data.SortTypes;
15866 // named sortTypes are supported, here we look them up
15867 if(typeof this.sortType == "string"){
15868 this.sortType = st[this.sortType];
15871 // set default sortType for strings and dates
15872 if(!this.sortType){
15875 this.sortType = st.asUCString;
15878 this.sortType = st.asDate;
15881 this.sortType = st.none;
15886 var stripRe = /[\$,%]/g;
15888 // prebuilt conversion function for this field, instead of
15889 // switching every time we're reading a value
15891 var cv, dateFormat = this.dateFormat;
15896 cv = function(v){ return v; };
15899 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15903 return v !== undefined && v !== null && v !== '' ?
15904 parseInt(String(v).replace(stripRe, ""), 10) : '';
15909 return v !== undefined && v !== null && v !== '' ?
15910 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15915 cv = function(v){ return v === true || v === "true" || v == 1; };
15922 if(v instanceof Date){
15926 if(dateFormat == "timestamp"){
15927 return new Date(v*1000);
15929 return Date.parseDate(v, dateFormat);
15931 var parsed = Date.parse(v);
15932 return parsed ? new Date(parsed) : null;
15941 Roo.data.Field.prototype = {
15949 * Ext JS Library 1.1.1
15950 * Copyright(c) 2006-2007, Ext JS, LLC.
15952 * Originally Released Under LGPL - original licence link has changed is not relivant.
15955 * <script type="text/javascript">
15958 // Base class for reading structured data from a data source. This class is intended to be
15959 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15962 * @class Roo.data.DataReader
15964 * Base class for reading structured data from a data source. This class is intended to be
15965 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15968 Roo.data.DataReader = function(meta, recordType){
15972 this.recordType = recordType instanceof Array ?
15973 Roo.data.Record.create(recordType) : recordType;
15976 Roo.data.DataReader.prototype = {
15979 readerType : 'Data',
15981 * Create an empty record
15982 * @param {Object} data (optional) - overlay some values
15983 * @return {Roo.data.Record} record created.
15985 newRow : function(d) {
15987 this.recordType.prototype.fields.each(function(c) {
15989 case 'int' : da[c.name] = 0; break;
15990 case 'date' : da[c.name] = new Date(); break;
15991 case 'float' : da[c.name] = 0.0; break;
15992 case 'boolean' : da[c.name] = false; break;
15993 default : da[c.name] = ""; break;
15997 return new this.recordType(Roo.apply(da, d));
16003 * Ext JS Library 1.1.1
16004 * Copyright(c) 2006-2007, Ext JS, LLC.
16006 * Originally Released Under LGPL - original licence link has changed is not relivant.
16009 * <script type="text/javascript">
16013 * @class Roo.data.DataProxy
16014 * @extends Roo.util.Observable
16016 * This class is an abstract base class for implementations which provide retrieval of
16017 * unformatted data objects.<br>
16019 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16020 * (of the appropriate type which knows how to parse the data object) to provide a block of
16021 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16023 * Custom implementations must implement the load method as described in
16024 * {@link Roo.data.HttpProxy#load}.
16026 Roo.data.DataProxy = function(){
16029 * @event beforeload
16030 * Fires before a network request is made to retrieve a data object.
16031 * @param {Object} This DataProxy object.
16032 * @param {Object} params The params parameter to the load function.
16037 * Fires before the load method's callback is called.
16038 * @param {Object} This DataProxy object.
16039 * @param {Object} o The data object.
16040 * @param {Object} arg The callback argument object passed to the load function.
16044 * @event loadexception
16045 * Fires if an Exception occurs during data retrieval.
16046 * @param {Object} This DataProxy object.
16047 * @param {Object} o The data object.
16048 * @param {Object} arg The callback argument object passed to the load function.
16049 * @param {Object} e The Exception.
16051 loadexception : true
16053 Roo.data.DataProxy.superclass.constructor.call(this);
16056 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16059 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16063 * Ext JS Library 1.1.1
16064 * Copyright(c) 2006-2007, Ext JS, LLC.
16066 * Originally Released Under LGPL - original licence link has changed is not relivant.
16069 * <script type="text/javascript">
16072 * @class Roo.data.MemoryProxy
16073 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16074 * to the Reader when its load method is called.
16076 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16078 Roo.data.MemoryProxy = function(data){
16082 Roo.data.MemoryProxy.superclass.constructor.call(this);
16086 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16089 * Load data from the requested source (in this case an in-memory
16090 * data object passed to the constructor), read the data object into
16091 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16092 * process that block using the passed callback.
16093 * @param {Object} params This parameter is not used by the MemoryProxy class.
16094 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16095 * object into a block of Roo.data.Records.
16096 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16097 * The function must be passed <ul>
16098 * <li>The Record block object</li>
16099 * <li>The "arg" argument from the load function</li>
16100 * <li>A boolean success indicator</li>
16102 * @param {Object} scope The scope in which to call the callback
16103 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16105 load : function(params, reader, callback, scope, arg){
16106 params = params || {};
16109 result = reader.readRecords(params.data ? params.data :this.data);
16111 this.fireEvent("loadexception", this, arg, null, e);
16112 callback.call(scope, null, arg, false);
16115 callback.call(scope, result, arg, true);
16119 update : function(params, records){
16124 * Ext JS Library 1.1.1
16125 * Copyright(c) 2006-2007, Ext JS, LLC.
16127 * Originally Released Under LGPL - original licence link has changed is not relivant.
16130 * <script type="text/javascript">
16133 * @class Roo.data.HttpProxy
16134 * @extends Roo.data.DataProxy
16135 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16136 * configured to reference a certain URL.<br><br>
16138 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16139 * from which the running page was served.<br><br>
16141 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16143 * Be aware that to enable the browser to parse an XML document, the server must set
16144 * the Content-Type header in the HTTP response to "text/xml".
16146 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16147 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16148 * will be used to make the request.
16150 Roo.data.HttpProxy = function(conn){
16151 Roo.data.HttpProxy.superclass.constructor.call(this);
16152 // is conn a conn config or a real conn?
16154 this.useAjax = !conn || !conn.events;
16158 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16159 // thse are take from connection...
16162 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16165 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16166 * extra parameters to each request made by this object. (defaults to undefined)
16169 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16170 * to each request made by this object. (defaults to undefined)
16173 * @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)
16176 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16179 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16185 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16189 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16190 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16191 * a finer-grained basis than the DataProxy events.
16193 getConnection : function(){
16194 return this.useAjax ? Roo.Ajax : this.conn;
16198 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16199 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16200 * process that block using the passed callback.
16201 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16202 * for the request to the remote server.
16203 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16204 * object into a block of Roo.data.Records.
16205 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16206 * The function must be passed <ul>
16207 * <li>The Record block object</li>
16208 * <li>The "arg" argument from the load function</li>
16209 * <li>A boolean success indicator</li>
16211 * @param {Object} scope The scope in which to call the callback
16212 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16214 load : function(params, reader, callback, scope, arg){
16215 if(this.fireEvent("beforeload", this, params) !== false){
16217 params : params || {},
16219 callback : callback,
16224 callback : this.loadResponse,
16228 Roo.applyIf(o, this.conn);
16229 if(this.activeRequest){
16230 Roo.Ajax.abort(this.activeRequest);
16232 this.activeRequest = Roo.Ajax.request(o);
16234 this.conn.request(o);
16237 callback.call(scope||this, null, arg, false);
16242 loadResponse : function(o, success, response){
16243 delete this.activeRequest;
16245 this.fireEvent("loadexception", this, o, response);
16246 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16251 result = o.reader.read(response);
16253 this.fireEvent("loadexception", this, o, response, e);
16254 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16258 this.fireEvent("load", this, o, o.request.arg);
16259 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16263 update : function(dataSet){
16268 updateResponse : function(dataSet){
16273 * Ext JS Library 1.1.1
16274 * Copyright(c) 2006-2007, Ext JS, LLC.
16276 * Originally Released Under LGPL - original licence link has changed is not relivant.
16279 * <script type="text/javascript">
16283 * @class Roo.data.ScriptTagProxy
16284 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16285 * other than the originating domain of the running page.<br><br>
16287 * <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
16288 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16290 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16291 * source code that is used as the source inside a <script> tag.<br><br>
16293 * In order for the browser to process the returned data, the server must wrap the data object
16294 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16295 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16296 * depending on whether the callback name was passed:
16299 boolean scriptTag = false;
16300 String cb = request.getParameter("callback");
16303 response.setContentType("text/javascript");
16305 response.setContentType("application/x-json");
16307 Writer out = response.getWriter();
16309 out.write(cb + "(");
16311 out.print(dataBlock.toJsonString());
16318 * @param {Object} config A configuration object.
16320 Roo.data.ScriptTagProxy = function(config){
16321 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16322 Roo.apply(this, config);
16323 this.head = document.getElementsByTagName("head")[0];
16326 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16328 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16330 * @cfg {String} url The URL from which to request the data object.
16333 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16337 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16338 * the server the name of the callback function set up by the load call to process the returned data object.
16339 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16340 * javascript output which calls this named function passing the data object as its only parameter.
16342 callbackParam : "callback",
16344 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16345 * name to the request.
16350 * Load data from the configured URL, read the data object into
16351 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16352 * process that block using the passed callback.
16353 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16354 * for the request to the remote server.
16355 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16356 * object into a block of Roo.data.Records.
16357 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16358 * The function must be passed <ul>
16359 * <li>The Record block object</li>
16360 * <li>The "arg" argument from the load function</li>
16361 * <li>A boolean success indicator</li>
16363 * @param {Object} scope The scope in which to call the callback
16364 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16366 load : function(params, reader, callback, scope, arg){
16367 if(this.fireEvent("beforeload", this, params) !== false){
16369 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16371 var url = this.url;
16372 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16374 url += "&_dc=" + (new Date().getTime());
16376 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16379 cb : "stcCallback"+transId,
16380 scriptId : "stcScript"+transId,
16384 callback : callback,
16390 window[trans.cb] = function(o){
16391 conn.handleResponse(o, trans);
16394 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16396 if(this.autoAbort !== false){
16400 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16402 var script = document.createElement("script");
16403 script.setAttribute("src", url);
16404 script.setAttribute("type", "text/javascript");
16405 script.setAttribute("id", trans.scriptId);
16406 this.head.appendChild(script);
16408 this.trans = trans;
16410 callback.call(scope||this, null, arg, false);
16415 isLoading : function(){
16416 return this.trans ? true : false;
16420 * Abort the current server request.
16422 abort : function(){
16423 if(this.isLoading()){
16424 this.destroyTrans(this.trans);
16429 destroyTrans : function(trans, isLoaded){
16430 this.head.removeChild(document.getElementById(trans.scriptId));
16431 clearTimeout(trans.timeoutId);
16433 window[trans.cb] = undefined;
16435 delete window[trans.cb];
16438 // if hasn't been loaded, wait for load to remove it to prevent script error
16439 window[trans.cb] = function(){
16440 window[trans.cb] = undefined;
16442 delete window[trans.cb];
16449 handleResponse : function(o, trans){
16450 this.trans = false;
16451 this.destroyTrans(trans, true);
16454 result = trans.reader.readRecords(o);
16456 this.fireEvent("loadexception", this, o, trans.arg, e);
16457 trans.callback.call(trans.scope||window, null, trans.arg, false);
16460 this.fireEvent("load", this, o, trans.arg);
16461 trans.callback.call(trans.scope||window, result, trans.arg, true);
16465 handleFailure : function(trans){
16466 this.trans = false;
16467 this.destroyTrans(trans, false);
16468 this.fireEvent("loadexception", this, null, trans.arg);
16469 trans.callback.call(trans.scope||window, null, trans.arg, false);
16473 * Ext JS Library 1.1.1
16474 * Copyright(c) 2006-2007, Ext JS, LLC.
16476 * Originally Released Under LGPL - original licence link has changed is not relivant.
16479 * <script type="text/javascript">
16483 * @class Roo.data.JsonReader
16484 * @extends Roo.data.DataReader
16485 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16486 * based on mappings in a provided Roo.data.Record constructor.
16488 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16489 * in the reply previously.
16494 var RecordDef = Roo.data.Record.create([
16495 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16496 {name: 'occupation'} // This field will use "occupation" as the mapping.
16498 var myReader = new Roo.data.JsonReader({
16499 totalProperty: "results", // The property which contains the total dataset size (optional)
16500 root: "rows", // The property which contains an Array of row objects
16501 id: "id" // The property within each row object that provides an ID for the record (optional)
16505 * This would consume a JSON file like this:
16507 { 'results': 2, 'rows': [
16508 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16509 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16512 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16513 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16514 * paged from the remote server.
16515 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16516 * @cfg {String} root name of the property which contains the Array of row objects.
16517 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16518 * @cfg {Array} fields Array of field definition objects
16520 * Create a new JsonReader
16521 * @param {Object} meta Metadata configuration options
16522 * @param {Object} recordType Either an Array of field definition objects,
16523 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16525 Roo.data.JsonReader = function(meta, recordType){
16528 // set some defaults:
16529 Roo.applyIf(meta, {
16530 totalProperty: 'total',
16531 successProperty : 'success',
16536 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16538 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16540 readerType : 'Json',
16543 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16544 * Used by Store query builder to append _requestMeta to params.
16547 metaFromRemote : false,
16549 * This method is only used by a DataProxy which has retrieved data from a remote server.
16550 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16551 * @return {Object} data A data block which is used by an Roo.data.Store object as
16552 * a cache of Roo.data.Records.
16554 read : function(response){
16555 var json = response.responseText;
16557 var o = /* eval:var:o */ eval("("+json+")");
16559 throw {message: "JsonReader.read: Json object not found"};
16565 this.metaFromRemote = true;
16566 this.meta = o.metaData;
16567 this.recordType = Roo.data.Record.create(o.metaData.fields);
16568 this.onMetaChange(this.meta, this.recordType, o);
16570 return this.readRecords(o);
16573 // private function a store will implement
16574 onMetaChange : function(meta, recordType, o){
16581 simpleAccess: function(obj, subsc) {
16588 getJsonAccessor: function(){
16590 return function(expr) {
16592 return(re.test(expr))
16593 ? new Function("obj", "return obj." + expr)
16598 return Roo.emptyFn;
16603 * Create a data block containing Roo.data.Records from an XML document.
16604 * @param {Object} o An object which contains an Array of row objects in the property specified
16605 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16606 * which contains the total size of the dataset.
16607 * @return {Object} data A data block which is used by an Roo.data.Store object as
16608 * a cache of Roo.data.Records.
16610 readRecords : function(o){
16612 * After any data loads, the raw JSON data is available for further custom processing.
16616 var s = this.meta, Record = this.recordType,
16617 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16619 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16621 if(s.totalProperty) {
16622 this.getTotal = this.getJsonAccessor(s.totalProperty);
16624 if(s.successProperty) {
16625 this.getSuccess = this.getJsonAccessor(s.successProperty);
16627 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16629 var g = this.getJsonAccessor(s.id);
16630 this.getId = function(rec) {
16632 return (r === undefined || r === "") ? null : r;
16635 this.getId = function(){return null;};
16638 for(var jj = 0; jj < fl; jj++){
16640 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16641 this.ef[jj] = this.getJsonAccessor(map);
16645 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16646 if(s.totalProperty){
16647 var vt = parseInt(this.getTotal(o), 10);
16652 if(s.successProperty){
16653 var vs = this.getSuccess(o);
16654 if(vs === false || vs === 'false'){
16659 for(var i = 0; i < c; i++){
16662 var id = this.getId(n);
16663 for(var j = 0; j < fl; j++){
16665 var v = this.ef[j](n);
16667 Roo.log('missing convert for ' + f.name);
16671 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16673 var record = new Record(values, id);
16675 records[i] = record;
16681 totalRecords : totalRecords
16684 // used when loading children.. @see loadDataFromChildren
16685 toLoadData: function(rec)
16687 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16688 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16689 return { data : data, total : data.length };
16694 * Ext JS Library 1.1.1
16695 * Copyright(c) 2006-2007, Ext JS, LLC.
16697 * Originally Released Under LGPL - original licence link has changed is not relivant.
16700 * <script type="text/javascript">
16704 * @class Roo.data.ArrayReader
16705 * @extends Roo.data.DataReader
16706 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16707 * Each element of that Array represents a row of data fields. The
16708 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16709 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16713 var RecordDef = Roo.data.Record.create([
16714 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16715 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16717 var myReader = new Roo.data.ArrayReader({
16718 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16722 * This would consume an Array like this:
16724 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16728 * Create a new JsonReader
16729 * @param {Object} meta Metadata configuration options.
16730 * @param {Object|Array} recordType Either an Array of field definition objects
16732 * @cfg {Array} fields Array of field definition objects
16733 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16734 * as specified to {@link Roo.data.Record#create},
16735 * or an {@link Roo.data.Record} object
16738 * created using {@link Roo.data.Record#create}.
16740 Roo.data.ArrayReader = function(meta, recordType)
16742 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16745 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16748 * Create a data block containing Roo.data.Records from an XML document.
16749 * @param {Object} o An Array of row objects which represents the dataset.
16750 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16751 * a cache of Roo.data.Records.
16753 readRecords : function(o)
16755 var sid = this.meta ? this.meta.id : null;
16756 var recordType = this.recordType, fields = recordType.prototype.fields;
16759 for(var i = 0; i < root.length; i++){
16762 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16763 for(var j = 0, jlen = fields.length; j < jlen; j++){
16764 var f = fields.items[j];
16765 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16766 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16768 values[f.name] = v;
16770 var record = new recordType(values, id);
16772 records[records.length] = record;
16776 totalRecords : records.length
16779 // used when loading children.. @see loadDataFromChildren
16780 toLoadData: function(rec)
16782 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16783 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16794 * @class Roo.bootstrap.ComboBox
16795 * @extends Roo.bootstrap.TriggerField
16796 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16797 * @cfg {Boolean} append (true|false) default false
16798 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16799 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16800 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16801 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16802 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16803 * @cfg {Boolean} animate default true
16804 * @cfg {Boolean} emptyResultText only for touch device
16805 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16806 * @cfg {String} emptyTitle default ''
16807 * @cfg {Number} width fixed with? experimental
16809 * Create a new ComboBox.
16810 * @param {Object} config Configuration options
16812 Roo.bootstrap.ComboBox = function(config){
16813 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16817 * Fires when the dropdown list is expanded
16818 * @param {Roo.bootstrap.ComboBox} combo This combo box
16823 * Fires when the dropdown list is collapsed
16824 * @param {Roo.bootstrap.ComboBox} combo This combo box
16828 * @event beforeselect
16829 * Fires before a list item is selected. Return false to cancel the selection.
16830 * @param {Roo.bootstrap.ComboBox} combo This combo box
16831 * @param {Roo.data.Record} record The data record returned from the underlying store
16832 * @param {Number} index The index of the selected item in the dropdown list
16834 'beforeselect' : true,
16837 * Fires when a list item is selected
16838 * @param {Roo.bootstrap.ComboBox} combo This combo box
16839 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16840 * @param {Number} index The index of the selected item in the dropdown list
16844 * @event beforequery
16845 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16846 * The event object passed has these properties:
16847 * @param {Roo.bootstrap.ComboBox} combo This combo box
16848 * @param {String} query The query
16849 * @param {Boolean} forceAll true to force "all" query
16850 * @param {Boolean} cancel true to cancel the query
16851 * @param {Object} e The query event object
16853 'beforequery': true,
16856 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16857 * @param {Roo.bootstrap.ComboBox} combo This combo box
16862 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16863 * @param {Roo.bootstrap.ComboBox} combo This combo box
16864 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16869 * Fires when the remove value from the combobox array
16870 * @param {Roo.bootstrap.ComboBox} combo This combo box
16874 * @event afterremove
16875 * Fires when the remove value from the combobox array
16876 * @param {Roo.bootstrap.ComboBox} combo This combo box
16878 'afterremove' : true,
16880 * @event specialfilter
16881 * Fires when specialfilter
16882 * @param {Roo.bootstrap.ComboBox} combo This combo box
16884 'specialfilter' : true,
16887 * Fires when tick the element
16888 * @param {Roo.bootstrap.ComboBox} combo This combo box
16892 * @event touchviewdisplay
16893 * Fires when touch view require special display (default is using displayField)
16894 * @param {Roo.bootstrap.ComboBox} combo This combo box
16895 * @param {Object} cfg set html .
16897 'touchviewdisplay' : true
16902 this.tickItems = [];
16904 this.selectedIndex = -1;
16905 if(this.mode == 'local'){
16906 if(config.queryDelay === undefined){
16907 this.queryDelay = 10;
16909 if(config.minChars === undefined){
16915 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16918 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16919 * rendering into an Roo.Editor, defaults to false)
16922 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16923 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16926 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16929 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16930 * the dropdown list (defaults to undefined, with no header element)
16934 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16938 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16940 listWidth: undefined,
16942 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16943 * mode = 'remote' or 'text' if mode = 'local')
16945 displayField: undefined,
16948 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16949 * mode = 'remote' or 'value' if mode = 'local').
16950 * Note: use of a valueField requires the user make a selection
16951 * in order for a value to be mapped.
16953 valueField: undefined,
16955 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16960 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16961 * field's data value (defaults to the underlying DOM element's name)
16963 hiddenName: undefined,
16965 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16969 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16971 selectedClass: 'active',
16974 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16978 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16979 * anchor positions (defaults to 'tl-bl')
16981 listAlign: 'tl-bl?',
16983 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16987 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16988 * query specified by the allQuery config option (defaults to 'query')
16990 triggerAction: 'query',
16992 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16993 * (defaults to 4, does not apply if editable = false)
16997 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16998 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17002 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17003 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17007 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17008 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17012 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17013 * when editable = true (defaults to false)
17015 selectOnFocus:false,
17017 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17019 queryParam: 'query',
17021 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17022 * when mode = 'remote' (defaults to 'Loading...')
17024 loadingText: 'Loading...',
17026 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17030 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17034 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17035 * traditional select (defaults to true)
17039 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17043 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17047 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17048 * listWidth has a higher value)
17052 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17053 * allow the user to set arbitrary text into the field (defaults to false)
17055 forceSelection:false,
17057 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17058 * if typeAhead = true (defaults to 250)
17060 typeAheadDelay : 250,
17062 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17063 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17065 valueNotFoundText : undefined,
17067 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17069 blockFocus : false,
17072 * @cfg {Boolean} disableClear Disable showing of clear button.
17074 disableClear : false,
17076 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17078 alwaysQuery : false,
17081 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17086 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17088 invalidClass : "has-warning",
17091 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17093 validClass : "has-success",
17096 * @cfg {Boolean} specialFilter (true|false) special filter default false
17098 specialFilter : false,
17101 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17103 mobileTouchView : true,
17106 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17108 useNativeIOS : false,
17111 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17113 mobile_restrict_height : false,
17115 ios_options : false,
17127 btnPosition : 'right',
17128 triggerList : true,
17129 showToggleBtn : true,
17131 emptyResultText: 'Empty',
17132 triggerText : 'Select',
17136 // element that contains real text value.. (when hidden is used..)
17138 getAutoCreate : function()
17143 * Render classic select for iso
17146 if(Roo.isIOS && this.useNativeIOS){
17147 cfg = this.getAutoCreateNativeIOS();
17155 if(Roo.isTouch && this.mobileTouchView){
17156 cfg = this.getAutoCreateTouchView();
17163 if(!this.tickable){
17164 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
17169 * ComboBox with tickable selections
17172 var align = this.labelAlign || this.parentLabelAlign();
17175 cls : 'form-group roo-combobox-tickable' //input-group
17178 var btn_text_select = '';
17179 var btn_text_done = '';
17180 var btn_text_cancel = '';
17182 if (this.btn_text_show) {
17183 btn_text_select = 'Select';
17184 btn_text_done = 'Done';
17185 btn_text_cancel = 'Cancel';
17190 cls : 'tickable-buttons',
17195 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17196 //html : this.triggerText
17197 html: btn_text_select
17203 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17205 html: btn_text_done
17211 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17213 html: btn_text_cancel
17219 buttons.cn.unshift({
17221 cls: 'roo-select2-search-field-input'
17227 Roo.each(buttons.cn, function(c){
17229 c.cls += ' btn-' + _this.size;
17232 if (_this.disabled) {
17239 style : 'display: contents',
17244 cls: 'form-hidden-field'
17248 cls: 'roo-select2-choices',
17252 cls: 'roo-select2-search-field',
17263 cls: 'roo-select2-container input-group roo-select2-container-multi',
17269 // cls: 'typeahead typeahead-long dropdown-menu',
17270 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17275 if(this.hasFeedback && !this.allowBlank){
17279 cls: 'glyphicon form-control-feedback'
17282 combobox.cn.push(feedback);
17289 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17290 tooltip : 'This field is required'
17292 if (Roo.bootstrap.version == 4) {
17295 style : 'display:none'
17298 if (align ==='left' && this.fieldLabel.length) {
17300 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17307 cls : 'control-label col-form-label',
17308 html : this.fieldLabel
17320 var labelCfg = cfg.cn[1];
17321 var contentCfg = cfg.cn[2];
17324 if(this.indicatorpos == 'right'){
17330 cls : 'control-label col-form-label',
17334 html : this.fieldLabel
17350 labelCfg = cfg.cn[0];
17351 contentCfg = cfg.cn[1];
17355 if(this.labelWidth > 12){
17356 labelCfg.style = "width: " + this.labelWidth + 'px';
17358 if(this.width * 1 > 0){
17359 contentCfg.style = "width: " + this.width + 'px';
17361 if(this.labelWidth < 13 && this.labelmd == 0){
17362 this.labelmd = this.labelWidth;
17365 if(this.labellg > 0){
17366 labelCfg.cls += ' col-lg-' + this.labellg;
17367 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17370 if(this.labelmd > 0){
17371 labelCfg.cls += ' col-md-' + this.labelmd;
17372 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17375 if(this.labelsm > 0){
17376 labelCfg.cls += ' col-sm-' + this.labelsm;
17377 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17380 if(this.labelxs > 0){
17381 labelCfg.cls += ' col-xs-' + this.labelxs;
17382 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17386 } else if ( this.fieldLabel.length) {
17387 // Roo.log(" label");
17392 //cls : 'input-group-addon',
17393 html : this.fieldLabel
17398 if(this.indicatorpos == 'right'){
17402 //cls : 'input-group-addon',
17403 html : this.fieldLabel
17413 // Roo.log(" no label && no align");
17420 ['xs','sm','md','lg'].map(function(size){
17421 if (settings[size]) {
17422 cfg.cls += ' col-' + size + '-' + settings[size];
17430 _initEventsCalled : false,
17433 initEvents: function()
17435 if (this._initEventsCalled) { // as we call render... prevent looping...
17438 this._initEventsCalled = true;
17441 throw "can not find store for combo";
17444 this.indicator = this.indicatorEl();
17446 this.store = Roo.factory(this.store, Roo.data);
17447 this.store.parent = this;
17449 // if we are building from html. then this element is so complex, that we can not really
17450 // use the rendered HTML.
17451 // so we have to trash and replace the previous code.
17452 if (Roo.XComponent.build_from_html) {
17453 // remove this element....
17454 var e = this.el.dom, k=0;
17455 while (e ) { e = e.previousSibling; ++k;}
17460 this.rendered = false;
17462 this.render(this.parent().getChildContainer(true), k);
17465 if(Roo.isIOS && this.useNativeIOS){
17466 this.initIOSView();
17474 if(Roo.isTouch && this.mobileTouchView){
17475 this.initTouchView();
17480 this.initTickableEvents();
17484 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17486 if(this.hiddenName){
17488 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17490 this.hiddenField.dom.value =
17491 this.hiddenValue !== undefined ? this.hiddenValue :
17492 this.value !== undefined ? this.value : '';
17494 // prevent input submission
17495 this.el.dom.removeAttribute('name');
17496 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17501 // this.el.dom.setAttribute('autocomplete', 'off');
17504 var cls = 'x-combo-list';
17506 //this.list = new Roo.Layer({
17507 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17513 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17514 _this.list.setWidth(lw);
17517 this.list.on('mouseover', this.onViewOver, this);
17518 this.list.on('mousemove', this.onViewMove, this);
17519 this.list.on('scroll', this.onViewScroll, this);
17522 this.list.swallowEvent('mousewheel');
17523 this.assetHeight = 0;
17526 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17527 this.assetHeight += this.header.getHeight();
17530 this.innerList = this.list.createChild({cls:cls+'-inner'});
17531 this.innerList.on('mouseover', this.onViewOver, this);
17532 this.innerList.on('mousemove', this.onViewMove, this);
17533 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17535 if(this.allowBlank && !this.pageSize && !this.disableClear){
17536 this.footer = this.list.createChild({cls:cls+'-ft'});
17537 this.pageTb = new Roo.Toolbar(this.footer);
17541 this.footer = this.list.createChild({cls:cls+'-ft'});
17542 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17543 {pageSize: this.pageSize});
17547 if (this.pageTb && this.allowBlank && !this.disableClear) {
17549 this.pageTb.add(new Roo.Toolbar.Fill(), {
17550 cls: 'x-btn-icon x-btn-clear',
17552 handler: function()
17555 _this.clearValue();
17556 _this.onSelect(false, -1);
17561 this.assetHeight += this.footer.getHeight();
17566 this.tpl = Roo.bootstrap.version == 4 ?
17567 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17568 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17571 this.view = new Roo.View(this.list, this.tpl, {
17572 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17574 //this.view.wrapEl.setDisplayed(false);
17575 this.view.on('click', this.onViewClick, this);
17578 this.store.on('beforeload', this.onBeforeLoad, this);
17579 this.store.on('load', this.onLoad, this);
17580 this.store.on('loadexception', this.onLoadException, this);
17582 if(this.resizable){
17583 this.resizer = new Roo.Resizable(this.list, {
17584 pinned:true, handles:'se'
17586 this.resizer.on('resize', function(r, w, h){
17587 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17588 this.listWidth = w;
17589 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17590 this.restrictHeight();
17592 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17595 if(!this.editable){
17596 this.editable = true;
17597 this.setEditable(false);
17602 if (typeof(this.events.add.listeners) != 'undefined') {
17604 this.addicon = this.wrap.createChild(
17605 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17607 this.addicon.on('click', function(e) {
17608 this.fireEvent('add', this);
17611 if (typeof(this.events.edit.listeners) != 'undefined') {
17613 this.editicon = this.wrap.createChild(
17614 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17615 if (this.addicon) {
17616 this.editicon.setStyle('margin-left', '40px');
17618 this.editicon.on('click', function(e) {
17620 // we fire even if inothing is selected..
17621 this.fireEvent('edit', this, this.lastData );
17627 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17628 "up" : function(e){
17629 this.inKeyMode = true;
17633 "down" : function(e){
17634 if(!this.isExpanded()){
17635 this.onTriggerClick();
17637 this.inKeyMode = true;
17642 "enter" : function(e){
17643 // this.onViewClick();
17647 if(this.fireEvent("specialkey", this, e)){
17648 this.onViewClick(false);
17654 "esc" : function(e){
17658 "tab" : function(e){
17661 if(this.fireEvent("specialkey", this, e)){
17662 this.onViewClick(false);
17670 doRelay : function(foo, bar, hname){
17671 if(hname == 'down' || this.scope.isExpanded()){
17672 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17681 this.queryDelay = Math.max(this.queryDelay || 10,
17682 this.mode == 'local' ? 10 : 250);
17685 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17687 if(this.typeAhead){
17688 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17690 if(this.editable !== false){
17691 this.inputEl().on("keyup", this.onKeyUp, this);
17693 if(this.forceSelection){
17694 this.inputEl().on('blur', this.doForce, this);
17698 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17699 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17703 initTickableEvents: function()
17707 if(this.hiddenName){
17709 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17711 this.hiddenField.dom.value =
17712 this.hiddenValue !== undefined ? this.hiddenValue :
17713 this.value !== undefined ? this.value : '';
17715 // prevent input submission
17716 this.el.dom.removeAttribute('name');
17717 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17722 // this.list = this.el.select('ul.dropdown-menu',true).first();
17724 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17725 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17726 if(this.triggerList){
17727 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17730 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17731 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17733 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17734 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17736 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17737 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17739 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17740 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17741 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17744 this.cancelBtn.hide();
17749 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17750 _this.list.setWidth(lw);
17753 this.list.on('mouseover', this.onViewOver, this);
17754 this.list.on('mousemove', this.onViewMove, this);
17756 this.list.on('scroll', this.onViewScroll, this);
17759 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17760 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17763 this.view = new Roo.View(this.list, this.tpl, {
17768 selectedClass: this.selectedClass
17771 //this.view.wrapEl.setDisplayed(false);
17772 this.view.on('click', this.onViewClick, this);
17776 this.store.on('beforeload', this.onBeforeLoad, this);
17777 this.store.on('load', this.onLoad, this);
17778 this.store.on('loadexception', this.onLoadException, this);
17781 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17782 "up" : function(e){
17783 this.inKeyMode = true;
17787 "down" : function(e){
17788 this.inKeyMode = true;
17792 "enter" : function(e){
17793 if(this.fireEvent("specialkey", this, e)){
17794 this.onViewClick(false);
17800 "esc" : function(e){
17801 this.onTickableFooterButtonClick(e, false, false);
17804 "tab" : function(e){
17805 this.fireEvent("specialkey", this, e);
17807 this.onTickableFooterButtonClick(e, false, false);
17814 doRelay : function(e, fn, key){
17815 if(this.scope.isExpanded()){
17816 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17825 this.queryDelay = Math.max(this.queryDelay || 10,
17826 this.mode == 'local' ? 10 : 250);
17829 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17831 if(this.typeAhead){
17832 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17835 if(this.editable !== false){
17836 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17839 this.indicator = this.indicatorEl();
17841 if(this.indicator){
17842 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17843 this.indicator.hide();
17848 onDestroy : function(){
17850 this.view.setStore(null);
17851 this.view.el.removeAllListeners();
17852 this.view.el.remove();
17853 this.view.purgeListeners();
17856 this.list.dom.innerHTML = '';
17860 this.store.un('beforeload', this.onBeforeLoad, this);
17861 this.store.un('load', this.onLoad, this);
17862 this.store.un('loadexception', this.onLoadException, this);
17864 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17868 fireKey : function(e){
17869 if(e.isNavKeyPress() && !this.list.isVisible()){
17870 this.fireEvent("specialkey", this, e);
17875 onResize: function(w, h)
17879 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17881 // if(typeof w != 'number'){
17882 // // we do not handle it!?!?
17885 // var tw = this.trigger.getWidth();
17886 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17887 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17889 // this.inputEl().setWidth( this.adjustWidth('input', x));
17891 // //this.trigger.setStyle('left', x+'px');
17893 // if(this.list && this.listWidth === undefined){
17894 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17895 // this.list.setWidth(lw);
17896 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17904 * Allow or prevent the user from directly editing the field text. If false is passed,
17905 * the user will only be able to select from the items defined in the dropdown list. This method
17906 * is the runtime equivalent of setting the 'editable' config option at config time.
17907 * @param {Boolean} value True to allow the user to directly edit the field text
17909 setEditable : function(value){
17910 if(value == this.editable){
17913 this.editable = value;
17915 this.inputEl().dom.setAttribute('readOnly', true);
17916 this.inputEl().on('mousedown', this.onTriggerClick, this);
17917 this.inputEl().addClass('x-combo-noedit');
17919 this.inputEl().dom.removeAttribute('readOnly');
17920 this.inputEl().un('mousedown', this.onTriggerClick, this);
17921 this.inputEl().removeClass('x-combo-noedit');
17927 onBeforeLoad : function(combo,opts){
17928 if(!this.hasFocus){
17932 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17934 this.restrictHeight();
17935 this.selectedIndex = -1;
17939 onLoad : function(){
17941 this.hasQuery = false;
17943 if(!this.hasFocus){
17947 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17948 this.loading.hide();
17951 if(this.store.getCount() > 0){
17954 this.restrictHeight();
17955 if(this.lastQuery == this.allQuery){
17956 if(this.editable && !this.tickable){
17957 this.inputEl().dom.select();
17961 !this.selectByValue(this.value, true) &&
17964 !this.store.lastOptions ||
17965 typeof(this.store.lastOptions.add) == 'undefined' ||
17966 this.store.lastOptions.add != true
17969 this.select(0, true);
17972 if(this.autoFocus){
17975 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17976 this.taTask.delay(this.typeAheadDelay);
17980 this.onEmptyResults();
17986 onLoadException : function()
17988 this.hasQuery = false;
17990 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17991 this.loading.hide();
17994 if(this.tickable && this.editable){
17999 // only causes errors at present
18000 //Roo.log(this.store.reader.jsonData);
18001 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18003 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18009 onTypeAhead : function(){
18010 if(this.store.getCount() > 0){
18011 var r = this.store.getAt(0);
18012 var newValue = r.data[this.displayField];
18013 var len = newValue.length;
18014 var selStart = this.getRawValue().length;
18016 if(selStart != len){
18017 this.setRawValue(newValue);
18018 this.selectText(selStart, newValue.length);
18024 onSelect : function(record, index){
18026 if(this.fireEvent('beforeselect', this, record, index) !== false){
18028 this.setFromData(index > -1 ? record.data : false);
18031 this.fireEvent('select', this, record, index);
18036 * Returns the currently selected field value or empty string if no value is set.
18037 * @return {String} value The selected value
18039 getValue : function()
18041 if(Roo.isIOS && this.useNativeIOS){
18042 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18046 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18049 if(this.valueField){
18050 return typeof this.value != 'undefined' ? this.value : '';
18052 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
18056 getRawValue : function()
18058 if(Roo.isIOS && this.useNativeIOS){
18059 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18062 var v = this.inputEl().getValue();
18068 * Clears any text/value currently set in the field
18070 clearValue : function(){
18072 if(this.hiddenField){
18073 this.hiddenField.dom.value = '';
18076 this.setRawValue('');
18077 this.lastSelectionText = '';
18078 this.lastData = false;
18080 var close = this.closeTriggerEl();
18091 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18092 * will be displayed in the field. If the value does not match the data value of an existing item,
18093 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18094 * Otherwise the field will be blank (although the value will still be set).
18095 * @param {String} value The value to match
18097 setValue : function(v)
18099 if(Roo.isIOS && this.useNativeIOS){
18100 this.setIOSValue(v);
18110 if(this.valueField){
18111 var r = this.findRecord(this.valueField, v);
18113 text = r.data[this.displayField];
18114 }else if(this.valueNotFoundText !== undefined){
18115 text = this.valueNotFoundText;
18118 this.lastSelectionText = text;
18119 if(this.hiddenField){
18120 this.hiddenField.dom.value = v;
18122 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
18125 var close = this.closeTriggerEl();
18128 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18134 * @property {Object} the last set data for the element
18139 * Sets the value of the field based on a object which is related to the record format for the store.
18140 * @param {Object} value the value to set as. or false on reset?
18142 setFromData : function(o){
18149 var dv = ''; // display value
18150 var vv = ''; // value value..
18152 if (this.displayField) {
18153 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18155 // this is an error condition!!!
18156 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18159 if(this.valueField){
18160 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18163 var close = this.closeTriggerEl();
18166 if(dv.length || vv * 1 > 0){
18168 this.blockFocus=true;
18174 if(this.hiddenField){
18175 this.hiddenField.dom.value = vv;
18177 this.lastSelectionText = dv;
18178 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
18182 // no hidden field.. - we store the value in 'value', but still display
18183 // display field!!!!
18184 this.lastSelectionText = dv;
18185 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
18192 reset : function(){
18193 // overridden so that last data is reset..
18200 this.setValue(this.originalValue);
18201 //this.clearInvalid();
18202 this.lastData = false;
18204 this.view.clearSelections();
18210 findRecord : function(prop, value){
18212 if(this.store.getCount() > 0){
18213 this.store.each(function(r){
18214 if(r.data[prop] == value){
18224 getName: function()
18226 // returns hidden if it's set..
18227 if (!this.rendered) {return ''};
18228 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18232 onViewMove : function(e, t){
18233 this.inKeyMode = false;
18237 onViewOver : function(e, t){
18238 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18241 var item = this.view.findItemFromChild(t);
18244 var index = this.view.indexOf(item);
18245 this.select(index, false);
18250 onViewClick : function(view, doFocus, el, e)
18252 var index = this.view.getSelectedIndexes()[0];
18254 var r = this.store.getAt(index);
18258 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18265 Roo.each(this.tickItems, function(v,k){
18267 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18269 _this.tickItems.splice(k, 1);
18271 if(typeof(e) == 'undefined' && view == false){
18272 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18284 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18285 this.tickItems.push(r.data);
18288 if(typeof(e) == 'undefined' && view == false){
18289 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18296 this.onSelect(r, index);
18298 if(doFocus !== false && !this.blockFocus){
18299 this.inputEl().focus();
18304 restrictHeight : function(){
18305 //this.innerList.dom.style.height = '';
18306 //var inner = this.innerList.dom;
18307 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18308 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18309 //this.list.beginUpdate();
18310 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18311 this.list.alignTo(this.inputEl(), this.listAlign);
18312 this.list.alignTo(this.inputEl(), this.listAlign);
18313 //this.list.endUpdate();
18317 onEmptyResults : function(){
18319 if(this.tickable && this.editable){
18320 this.hasFocus = false;
18321 this.restrictHeight();
18329 * Returns true if the dropdown list is expanded, else false.
18331 isExpanded : function(){
18332 return this.list.isVisible();
18336 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18337 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18338 * @param {String} value The data value of the item to select
18339 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18340 * selected item if it is not currently in view (defaults to true)
18341 * @return {Boolean} True if the value matched an item in the list, else false
18343 selectByValue : function(v, scrollIntoView){
18344 if(v !== undefined && v !== null){
18345 var r = this.findRecord(this.valueField || this.displayField, v);
18347 this.select(this.store.indexOf(r), scrollIntoView);
18355 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18356 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18357 * @param {Number} index The zero-based index of the list item to select
18358 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18359 * selected item if it is not currently in view (defaults to true)
18361 select : function(index, scrollIntoView){
18362 this.selectedIndex = index;
18363 this.view.select(index);
18364 if(scrollIntoView !== false){
18365 var el = this.view.getNode(index);
18367 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18370 this.list.scrollChildIntoView(el, false);
18376 selectNext : function(){
18377 var ct = this.store.getCount();
18379 if(this.selectedIndex == -1){
18381 }else if(this.selectedIndex < ct-1){
18382 this.select(this.selectedIndex+1);
18388 selectPrev : function(){
18389 var ct = this.store.getCount();
18391 if(this.selectedIndex == -1){
18393 }else if(this.selectedIndex != 0){
18394 this.select(this.selectedIndex-1);
18400 onKeyUp : function(e){
18401 if(this.editable !== false && !e.isSpecialKey()){
18402 this.lastKey = e.getKey();
18403 this.dqTask.delay(this.queryDelay);
18408 validateBlur : function(){
18409 return !this.list || !this.list.isVisible();
18413 initQuery : function(){
18415 var v = this.getRawValue();
18417 if(this.tickable && this.editable){
18418 v = this.tickableInputEl().getValue();
18425 doForce : function(){
18426 if(this.inputEl().dom.value.length > 0){
18427 this.inputEl().dom.value =
18428 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18434 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18435 * query allowing the query action to be canceled if needed.
18436 * @param {String} query The SQL query to execute
18437 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18438 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18439 * saved in the current store (defaults to false)
18441 doQuery : function(q, forceAll){
18443 if(q === undefined || q === null){
18448 forceAll: forceAll,
18452 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18457 forceAll = qe.forceAll;
18458 if(forceAll === true || (q.length >= this.minChars)){
18460 this.hasQuery = true;
18462 if(this.lastQuery != q || this.alwaysQuery){
18463 this.lastQuery = q;
18464 if(this.mode == 'local'){
18465 this.selectedIndex = -1;
18467 this.store.clearFilter();
18470 if(this.specialFilter){
18471 this.fireEvent('specialfilter', this);
18476 this.store.filter(this.displayField, q);
18479 this.store.fireEvent("datachanged", this.store);
18486 this.store.baseParams[this.queryParam] = q;
18488 var options = {params : this.getParams(q)};
18491 options.add = true;
18492 options.params.start = this.page * this.pageSize;
18495 this.store.load(options);
18498 * this code will make the page width larger, at the beginning, the list not align correctly,
18499 * we should expand the list on onLoad
18500 * so command out it
18505 this.selectedIndex = -1;
18510 this.loadNext = false;
18514 getParams : function(q){
18516 //p[this.queryParam] = q;
18520 p.limit = this.pageSize;
18526 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18528 collapse : function(){
18529 if(!this.isExpanded()){
18535 this.hasFocus = false;
18539 this.cancelBtn.hide();
18540 this.trigger.show();
18543 this.tickableInputEl().dom.value = '';
18544 this.tickableInputEl().blur();
18549 Roo.get(document).un('mousedown', this.collapseIf, this);
18550 Roo.get(document).un('mousewheel', this.collapseIf, this);
18551 if (!this.editable) {
18552 Roo.get(document).un('keydown', this.listKeyPress, this);
18554 this.fireEvent('collapse', this);
18560 collapseIf : function(e){
18561 var in_combo = e.within(this.el);
18562 var in_list = e.within(this.list);
18563 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18565 if (in_combo || in_list || is_list) {
18566 //e.stopPropagation();
18571 this.onTickableFooterButtonClick(e, false, false);
18579 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18581 expand : function(){
18583 if(this.isExpanded() || !this.hasFocus){
18587 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18588 this.list.setWidth(lw);
18594 this.restrictHeight();
18598 this.tickItems = Roo.apply([], this.item);
18601 this.cancelBtn.show();
18602 this.trigger.hide();
18605 this.tickableInputEl().focus();
18610 Roo.get(document).on('mousedown', this.collapseIf, this);
18611 Roo.get(document).on('mousewheel', this.collapseIf, this);
18612 if (!this.editable) {
18613 Roo.get(document).on('keydown', this.listKeyPress, this);
18616 this.fireEvent('expand', this);
18620 // Implements the default empty TriggerField.onTriggerClick function
18621 onTriggerClick : function(e)
18623 Roo.log('trigger click');
18625 if(this.disabled || !this.triggerList){
18630 this.loadNext = false;
18632 if(this.isExpanded()){
18634 if (!this.blockFocus) {
18635 this.inputEl().focus();
18639 this.hasFocus = true;
18640 if(this.triggerAction == 'all') {
18641 this.doQuery(this.allQuery, true);
18643 this.doQuery(this.getRawValue());
18645 if (!this.blockFocus) {
18646 this.inputEl().focus();
18651 onTickableTriggerClick : function(e)
18658 this.loadNext = false;
18659 this.hasFocus = true;
18661 if(this.triggerAction == 'all') {
18662 this.doQuery(this.allQuery, true);
18664 this.doQuery(this.getRawValue());
18668 onSearchFieldClick : function(e)
18670 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18671 this.onTickableFooterButtonClick(e, false, false);
18675 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18680 this.loadNext = false;
18681 this.hasFocus = true;
18683 if(this.triggerAction == 'all') {
18684 this.doQuery(this.allQuery, true);
18686 this.doQuery(this.getRawValue());
18690 listKeyPress : function(e)
18692 //Roo.log('listkeypress');
18693 // scroll to first matching element based on key pres..
18694 if (e.isSpecialKey()) {
18697 var k = String.fromCharCode(e.getKey()).toUpperCase();
18700 var csel = this.view.getSelectedNodes();
18701 var cselitem = false;
18703 var ix = this.view.indexOf(csel[0]);
18704 cselitem = this.store.getAt(ix);
18705 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18711 this.store.each(function(v) {
18713 // start at existing selection.
18714 if (cselitem.id == v.id) {
18720 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18721 match = this.store.indexOf(v);
18727 if (match === false) {
18728 return true; // no more action?
18731 this.view.select(match);
18732 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18733 sn.scrollIntoView(sn.dom.parentNode, false);
18736 onViewScroll : function(e, t){
18738 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){
18742 this.hasQuery = true;
18744 this.loading = this.list.select('.loading', true).first();
18746 if(this.loading === null){
18747 this.list.createChild({
18749 cls: 'loading roo-select2-more-results roo-select2-active',
18750 html: 'Loading more results...'
18753 this.loading = this.list.select('.loading', true).first();
18755 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18757 this.loading.hide();
18760 this.loading.show();
18765 this.loadNext = true;
18767 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18772 addItem : function(o)
18774 var dv = ''; // display value
18776 if (this.displayField) {
18777 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18779 // this is an error condition!!!
18780 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18787 var choice = this.choices.createChild({
18789 cls: 'roo-select2-search-choice',
18798 cls: 'roo-select2-search-choice-close fa fa-times',
18803 }, this.searchField);
18805 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18807 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18815 this.inputEl().dom.value = '';
18820 onRemoveItem : function(e, _self, o)
18822 e.preventDefault();
18824 this.lastItem = Roo.apply([], this.item);
18826 var index = this.item.indexOf(o.data) * 1;
18829 Roo.log('not this item?!');
18833 this.item.splice(index, 1);
18838 this.fireEvent('remove', this, e);
18844 syncValue : function()
18846 if(!this.item.length){
18853 Roo.each(this.item, function(i){
18854 if(_this.valueField){
18855 value.push(i[_this.valueField]);
18862 this.value = value.join(',');
18864 if(this.hiddenField){
18865 this.hiddenField.dom.value = this.value;
18868 this.store.fireEvent("datachanged", this.store);
18873 clearItem : function()
18875 if(!this.multiple){
18881 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18889 if(this.tickable && !Roo.isTouch){
18890 this.view.refresh();
18894 inputEl: function ()
18896 if(Roo.isIOS && this.useNativeIOS){
18897 return this.el.select('select.roo-ios-select', true).first();
18900 if(Roo.isTouch && this.mobileTouchView){
18901 return this.el.select('input.form-control',true).first();
18905 return this.searchField;
18908 return this.el.select('input.form-control',true).first();
18911 onTickableFooterButtonClick : function(e, btn, el)
18913 e.preventDefault();
18915 this.lastItem = Roo.apply([], this.item);
18917 if(btn && btn.name == 'cancel'){
18918 this.tickItems = Roo.apply([], this.item);
18927 Roo.each(this.tickItems, function(o){
18935 validate : function()
18937 if(this.getVisibilityEl().hasClass('hidden')){
18941 var v = this.getRawValue();
18944 v = this.getValue();
18947 if(this.disabled || this.allowBlank || v.length){
18952 this.markInvalid();
18956 tickableInputEl : function()
18958 if(!this.tickable || !this.editable){
18959 return this.inputEl();
18962 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18966 getAutoCreateTouchView : function()
18971 cls: 'form-group' //input-group
18977 type : this.inputType,
18978 cls : 'form-control x-combo-noedit',
18979 autocomplete: 'new-password',
18980 placeholder : this.placeholder || '',
18985 input.name = this.name;
18989 input.cls += ' input-' + this.size;
18992 if (this.disabled) {
18993 input.disabled = true;
18997 cls : 'roo-combobox-wrap',
19004 inputblock.cls += ' input-group';
19006 inputblock.cn.unshift({
19008 cls : 'input-group-addon input-group-prepend input-group-text',
19013 if(this.removable && !this.multiple){
19014 inputblock.cls += ' roo-removable';
19016 inputblock.cn.push({
19019 cls : 'roo-combo-removable-btn close'
19023 if(this.hasFeedback && !this.allowBlank){
19025 inputblock.cls += ' has-feedback';
19027 inputblock.cn.push({
19029 cls: 'glyphicon form-control-feedback'
19036 inputblock.cls += (this.before) ? '' : ' input-group';
19038 inputblock.cn.push({
19040 cls : 'input-group-addon input-group-append input-group-text',
19046 var ibwrap = inputblock;
19051 cls: 'roo-select2-choices',
19055 cls: 'roo-select2-search-field',
19068 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19073 cls: 'form-hidden-field'
19079 if(!this.multiple && this.showToggleBtn){
19085 if (this.caret != false) {
19088 cls: 'fa fa-' + this.caret
19095 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19097 Roo.bootstrap.version == 3 ? caret : '',
19100 cls: 'combobox-clear',
19114 combobox.cls += ' roo-select2-container-multi';
19117 var required = this.allowBlank ? {
19119 style: 'display: none'
19122 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19123 tooltip : 'This field is required'
19126 var align = this.labelAlign || this.parentLabelAlign();
19128 if (align ==='left' && this.fieldLabel.length) {
19134 cls : 'control-label col-form-label',
19135 html : this.fieldLabel
19139 cls : 'roo-combobox-wrap ',
19146 var labelCfg = cfg.cn[1];
19147 var contentCfg = cfg.cn[2];
19150 if(this.indicatorpos == 'right'){
19155 cls : 'control-label col-form-label',
19159 html : this.fieldLabel
19165 cls : "roo-combobox-wrap ",
19173 labelCfg = cfg.cn[0];
19174 contentCfg = cfg.cn[1];
19179 if(this.labelWidth > 12){
19180 labelCfg.style = "width: " + this.labelWidth + 'px';
19183 if(this.labelWidth < 13 && this.labelmd == 0){
19184 this.labelmd = this.labelWidth;
19187 if(this.labellg > 0){
19188 labelCfg.cls += ' col-lg-' + this.labellg;
19189 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19192 if(this.labelmd > 0){
19193 labelCfg.cls += ' col-md-' + this.labelmd;
19194 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19197 if(this.labelsm > 0){
19198 labelCfg.cls += ' col-sm-' + this.labelsm;
19199 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19202 if(this.labelxs > 0){
19203 labelCfg.cls += ' col-xs-' + this.labelxs;
19204 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19208 } else if ( this.fieldLabel.length) {
19213 cls : 'control-label',
19214 html : this.fieldLabel
19225 if(this.indicatorpos == 'right'){
19229 cls : 'control-label',
19230 html : this.fieldLabel,
19248 var settings = this;
19250 ['xs','sm','md','lg'].map(function(size){
19251 if (settings[size]) {
19252 cfg.cls += ' col-' + size + '-' + settings[size];
19259 initTouchView : function()
19261 this.renderTouchView();
19263 this.touchViewEl.on('scroll', function(){
19264 this.el.dom.scrollTop = 0;
19267 this.originalValue = this.getValue();
19269 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19271 this.inputEl().on("click", this.showTouchView, this);
19272 if (this.triggerEl) {
19273 this.triggerEl.on("click", this.showTouchView, this);
19277 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19278 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19280 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19282 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19283 this.store.on('load', this.onTouchViewLoad, this);
19284 this.store.on('loadexception', this.onTouchViewLoadException, this);
19286 if(this.hiddenName){
19288 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19290 this.hiddenField.dom.value =
19291 this.hiddenValue !== undefined ? this.hiddenValue :
19292 this.value !== undefined ? this.value : '';
19294 this.el.dom.removeAttribute('name');
19295 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19299 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19300 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19303 if(this.removable && !this.multiple){
19304 var close = this.closeTriggerEl();
19306 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19307 close.on('click', this.removeBtnClick, this, close);
19311 * fix the bug in Safari iOS8
19313 this.inputEl().on("focus", function(e){
19314 document.activeElement.blur();
19317 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19324 renderTouchView : function()
19326 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
19327 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19329 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19330 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19332 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19333 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19334 this.touchViewBodyEl.setStyle('overflow', 'auto');
19336 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19337 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19339 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19340 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19344 showTouchView : function()
19350 this.touchViewHeaderEl.hide();
19352 if(this.modalTitle.length){
19353 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19354 this.touchViewHeaderEl.show();
19357 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19358 this.touchViewEl.show();
19360 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19362 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19363 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19365 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19367 if(this.modalTitle.length){
19368 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19371 this.touchViewBodyEl.setHeight(bodyHeight);
19375 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19377 this.touchViewEl.addClass(['in','show']);
19380 if(this._touchViewMask){
19381 Roo.get(document.body).addClass("x-body-masked");
19382 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19383 this._touchViewMask.setStyle('z-index', 10000);
19384 this._touchViewMask.addClass('show');
19387 this.doTouchViewQuery();
19391 hideTouchView : function()
19393 this.touchViewEl.removeClass(['in','show']);
19397 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19399 this.touchViewEl.setStyle('display', 'none');
19402 if(this._touchViewMask){
19403 this._touchViewMask.removeClass('show');
19404 Roo.get(document.body).removeClass("x-body-masked");
19408 setTouchViewValue : function()
19415 Roo.each(this.tickItems, function(o){
19420 this.hideTouchView();
19423 doTouchViewQuery : function()
19432 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19436 if(!this.alwaysQuery || this.mode == 'local'){
19437 this.onTouchViewLoad();
19444 onTouchViewBeforeLoad : function(combo,opts)
19450 onTouchViewLoad : function()
19452 if(this.store.getCount() < 1){
19453 this.onTouchViewEmptyResults();
19457 this.clearTouchView();
19459 var rawValue = this.getRawValue();
19461 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19463 this.tickItems = [];
19465 this.store.data.each(function(d, rowIndex){
19466 var row = this.touchViewListGroup.createChild(template);
19468 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19469 row.addClass(d.data.cls);
19472 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19475 html : d.data[this.displayField]
19478 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19479 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19482 row.removeClass('selected');
19483 if(!this.multiple && this.valueField &&
19484 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19487 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19488 row.addClass('selected');
19491 if(this.multiple && this.valueField &&
19492 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19496 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19497 this.tickItems.push(d.data);
19500 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19504 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19506 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19508 if(this.modalTitle.length){
19509 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19512 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19514 if(this.mobile_restrict_height && listHeight < bodyHeight){
19515 this.touchViewBodyEl.setHeight(listHeight);
19520 if(firstChecked && listHeight > bodyHeight){
19521 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19526 onTouchViewLoadException : function()
19528 this.hideTouchView();
19531 onTouchViewEmptyResults : function()
19533 this.clearTouchView();
19535 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19537 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19541 clearTouchView : function()
19543 this.touchViewListGroup.dom.innerHTML = '';
19546 onTouchViewClick : function(e, el, o)
19548 e.preventDefault();
19551 var rowIndex = o.rowIndex;
19553 var r = this.store.getAt(rowIndex);
19555 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19557 if(!this.multiple){
19558 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19559 c.dom.removeAttribute('checked');
19562 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19564 this.setFromData(r.data);
19566 var close = this.closeTriggerEl();
19572 this.hideTouchView();
19574 this.fireEvent('select', this, r, rowIndex);
19579 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19580 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19581 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19585 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19586 this.addItem(r.data);
19587 this.tickItems.push(r.data);
19591 getAutoCreateNativeIOS : function()
19594 cls: 'form-group' //input-group,
19599 cls : 'roo-ios-select'
19603 combobox.name = this.name;
19606 if (this.disabled) {
19607 combobox.disabled = true;
19610 var settings = this;
19612 ['xs','sm','md','lg'].map(function(size){
19613 if (settings[size]) {
19614 cfg.cls += ' col-' + size + '-' + settings[size];
19624 initIOSView : function()
19626 this.store.on('load', this.onIOSViewLoad, this);
19631 onIOSViewLoad : function()
19633 if(this.store.getCount() < 1){
19637 this.clearIOSView();
19639 if(this.allowBlank) {
19641 var default_text = '-- SELECT --';
19643 if(this.placeholder.length){
19644 default_text = this.placeholder;
19647 if(this.emptyTitle.length){
19648 default_text += ' - ' + this.emptyTitle + ' -';
19651 var opt = this.inputEl().createChild({
19654 html : default_text
19658 o[this.valueField] = 0;
19659 o[this.displayField] = default_text;
19661 this.ios_options.push({
19668 this.store.data.each(function(d, rowIndex){
19672 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19673 html = d.data[this.displayField];
19678 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19679 value = d.data[this.valueField];
19688 if(this.value == d.data[this.valueField]){
19689 option['selected'] = true;
19692 var opt = this.inputEl().createChild(option);
19694 this.ios_options.push({
19701 this.inputEl().on('change', function(){
19702 this.fireEvent('select', this);
19707 clearIOSView: function()
19709 this.inputEl().dom.innerHTML = '';
19711 this.ios_options = [];
19714 setIOSValue: function(v)
19718 if(!this.ios_options){
19722 Roo.each(this.ios_options, function(opts){
19724 opts.el.dom.removeAttribute('selected');
19726 if(opts.data[this.valueField] != v){
19730 opts.el.dom.setAttribute('selected', true);
19736 * @cfg {Boolean} grow
19740 * @cfg {Number} growMin
19744 * @cfg {Number} growMax
19753 Roo.apply(Roo.bootstrap.ComboBox, {
19757 cls: 'modal-header',
19779 cls: 'list-group-item',
19783 cls: 'roo-combobox-list-group-item-value'
19787 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19801 listItemCheckbox : {
19803 cls: 'list-group-item',
19807 cls: 'roo-combobox-list-group-item-value'
19811 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19827 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19832 cls: 'modal-footer',
19840 cls: 'col-xs-6 text-left',
19843 cls: 'btn btn-danger roo-touch-view-cancel',
19849 cls: 'col-xs-6 text-right',
19852 cls: 'btn btn-success roo-touch-view-ok',
19863 Roo.apply(Roo.bootstrap.ComboBox, {
19865 touchViewTemplate : {
19867 cls: 'modal fade roo-combobox-touch-view',
19871 cls: 'modal-dialog',
19872 style : 'position:fixed', // we have to fix position....
19876 cls: 'modal-content',
19878 Roo.bootstrap.ComboBox.header,
19879 Roo.bootstrap.ComboBox.body,
19880 Roo.bootstrap.ComboBox.footer
19889 * Ext JS Library 1.1.1
19890 * Copyright(c) 2006-2007, Ext JS, LLC.
19892 * Originally Released Under LGPL - original licence link has changed is not relivant.
19895 * <script type="text/javascript">
19900 * @extends Roo.util.Observable
19901 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19902 * This class also supports single and multi selection modes. <br>
19903 * Create a data model bound view:
19905 var store = new Roo.data.Store(...);
19907 var view = new Roo.View({
19909 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19911 singleSelect: true,
19912 selectedClass: "ydataview-selected",
19916 // listen for node click?
19917 view.on("click", function(vw, index, node, e){
19918 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19922 dataModel.load("foobar.xml");
19924 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19926 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19927 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19929 * Note: old style constructor is still suported (container, template, config)
19932 * Create a new View
19933 * @param {Object} config The config object
19936 Roo.View = function(config, depreciated_tpl, depreciated_config){
19938 this.parent = false;
19940 if (typeof(depreciated_tpl) == 'undefined') {
19941 // new way.. - universal constructor.
19942 Roo.apply(this, config);
19943 this.el = Roo.get(this.el);
19946 this.el = Roo.get(config);
19947 this.tpl = depreciated_tpl;
19948 Roo.apply(this, depreciated_config);
19950 this.wrapEl = this.el.wrap().wrap();
19951 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19954 if(typeof(this.tpl) == "string"){
19955 this.tpl = new Roo.Template(this.tpl);
19957 // support xtype ctors..
19958 this.tpl = new Roo.factory(this.tpl, Roo);
19962 this.tpl.compile();
19967 * @event beforeclick
19968 * Fires before a click is processed. Returns false to cancel the default action.
19969 * @param {Roo.View} this
19970 * @param {Number} index The index of the target node
19971 * @param {HTMLElement} node The target node
19972 * @param {Roo.EventObject} e The raw event object
19974 "beforeclick" : true,
19977 * Fires when a template node is clicked.
19978 * @param {Roo.View} this
19979 * @param {Number} index The index of the target node
19980 * @param {HTMLElement} node The target node
19981 * @param {Roo.EventObject} e The raw event object
19986 * Fires when a template node is double clicked.
19987 * @param {Roo.View} this
19988 * @param {Number} index The index of the target node
19989 * @param {HTMLElement} node The target node
19990 * @param {Roo.EventObject} e The raw event object
19994 * @event contextmenu
19995 * Fires when a template node is right clicked.
19996 * @param {Roo.View} this
19997 * @param {Number} index The index of the target node
19998 * @param {HTMLElement} node The target node
19999 * @param {Roo.EventObject} e The raw event object
20001 "contextmenu" : true,
20003 * @event selectionchange
20004 * Fires when the selected nodes change.
20005 * @param {Roo.View} this
20006 * @param {Array} selections Array of the selected nodes
20008 "selectionchange" : true,
20011 * @event beforeselect
20012 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20013 * @param {Roo.View} this
20014 * @param {HTMLElement} node The node to be selected
20015 * @param {Array} selections Array of currently selected nodes
20017 "beforeselect" : true,
20019 * @event preparedata
20020 * Fires on every row to render, to allow you to change the data.
20021 * @param {Roo.View} this
20022 * @param {Object} data to be rendered (change this)
20024 "preparedata" : true
20032 "click": this.onClick,
20033 "dblclick": this.onDblClick,
20034 "contextmenu": this.onContextMenu,
20038 this.selections = [];
20040 this.cmp = new Roo.CompositeElementLite([]);
20042 this.store = Roo.factory(this.store, Roo.data);
20043 this.setStore(this.store, true);
20046 if ( this.footer && this.footer.xtype) {
20048 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20050 this.footer.dataSource = this.store;
20051 this.footer.container = fctr;
20052 this.footer = Roo.factory(this.footer, Roo);
20053 fctr.insertFirst(this.el);
20055 // this is a bit insane - as the paging toolbar seems to detach the el..
20056 // dom.parentNode.parentNode.parentNode
20057 // they get detached?
20061 Roo.View.superclass.constructor.call(this);
20066 Roo.extend(Roo.View, Roo.util.Observable, {
20069 * @cfg {Roo.data.Store} store Data store to load data from.
20074 * @cfg {String|Roo.Element} el The container element.
20079 * @cfg {String|Roo.Template} tpl The template used by this View
20083 * @cfg {String} dataName the named area of the template to use as the data area
20084 * Works with domtemplates roo-name="name"
20088 * @cfg {String} selectedClass The css class to add to selected nodes
20090 selectedClass : "x-view-selected",
20092 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20097 * @cfg {String} text to display on mask (default Loading)
20101 * @cfg {Boolean} multiSelect Allow multiple selection
20103 multiSelect : false,
20105 * @cfg {Boolean} singleSelect Allow single selection
20107 singleSelect: false,
20110 * @cfg {Boolean} toggleSelect - selecting
20112 toggleSelect : false,
20115 * @cfg {Boolean} tickable - selecting
20120 * Returns the element this view is bound to.
20121 * @return {Roo.Element}
20123 getEl : function(){
20124 return this.wrapEl;
20130 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20132 refresh : function(){
20133 //Roo.log('refresh');
20136 // if we are using something like 'domtemplate', then
20137 // the what gets used is:
20138 // t.applySubtemplate(NAME, data, wrapping data..)
20139 // the outer template then get' applied with
20140 // the store 'extra data'
20141 // and the body get's added to the
20142 // roo-name="data" node?
20143 // <span class='roo-tpl-{name}'></span> ?????
20147 this.clearSelections();
20148 this.el.update("");
20150 var records = this.store.getRange();
20151 if(records.length < 1) {
20153 // is this valid?? = should it render a template??
20155 this.el.update(this.emptyText);
20159 if (this.dataName) {
20160 this.el.update(t.apply(this.store.meta)); //????
20161 el = this.el.child('.roo-tpl-' + this.dataName);
20164 for(var i = 0, len = records.length; i < len; i++){
20165 var data = this.prepareData(records[i].data, i, records[i]);
20166 this.fireEvent("preparedata", this, data, i, records[i]);
20168 var d = Roo.apply({}, data);
20171 Roo.apply(d, {'roo-id' : Roo.id()});
20175 Roo.each(this.parent.item, function(item){
20176 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20179 Roo.apply(d, {'roo-data-checked' : 'checked'});
20183 html[html.length] = Roo.util.Format.trim(
20185 t.applySubtemplate(this.dataName, d, this.store.meta) :
20192 el.update(html.join(""));
20193 this.nodes = el.dom.childNodes;
20194 this.updateIndexes(0);
20199 * Function to override to reformat the data that is sent to
20200 * the template for each node.
20201 * DEPRICATED - use the preparedata event handler.
20202 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20203 * a JSON object for an UpdateManager bound view).
20205 prepareData : function(data, index, record)
20207 this.fireEvent("preparedata", this, data, index, record);
20211 onUpdate : function(ds, record){
20212 // Roo.log('on update');
20213 this.clearSelections();
20214 var index = this.store.indexOf(record);
20215 var n = this.nodes[index];
20216 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20217 n.parentNode.removeChild(n);
20218 this.updateIndexes(index, index);
20224 onAdd : function(ds, records, index)
20226 //Roo.log(['on Add', ds, records, index] );
20227 this.clearSelections();
20228 if(this.nodes.length == 0){
20232 var n = this.nodes[index];
20233 for(var i = 0, len = records.length; i < len; i++){
20234 var d = this.prepareData(records[i].data, i, records[i]);
20236 this.tpl.insertBefore(n, d);
20239 this.tpl.append(this.el, d);
20242 this.updateIndexes(index);
20245 onRemove : function(ds, record, index){
20246 // Roo.log('onRemove');
20247 this.clearSelections();
20248 var el = this.dataName ?
20249 this.el.child('.roo-tpl-' + this.dataName) :
20252 el.dom.removeChild(this.nodes[index]);
20253 this.updateIndexes(index);
20257 * Refresh an individual node.
20258 * @param {Number} index
20260 refreshNode : function(index){
20261 this.onUpdate(this.store, this.store.getAt(index));
20264 updateIndexes : function(startIndex, endIndex){
20265 var ns = this.nodes;
20266 startIndex = startIndex || 0;
20267 endIndex = endIndex || ns.length - 1;
20268 for(var i = startIndex; i <= endIndex; i++){
20269 ns[i].nodeIndex = i;
20274 * Changes the data store this view uses and refresh the view.
20275 * @param {Store} store
20277 setStore : function(store, initial){
20278 if(!initial && this.store){
20279 this.store.un("datachanged", this.refresh);
20280 this.store.un("add", this.onAdd);
20281 this.store.un("remove", this.onRemove);
20282 this.store.un("update", this.onUpdate);
20283 this.store.un("clear", this.refresh);
20284 this.store.un("beforeload", this.onBeforeLoad);
20285 this.store.un("load", this.onLoad);
20286 this.store.un("loadexception", this.onLoad);
20290 store.on("datachanged", this.refresh, this);
20291 store.on("add", this.onAdd, this);
20292 store.on("remove", this.onRemove, this);
20293 store.on("update", this.onUpdate, this);
20294 store.on("clear", this.refresh, this);
20295 store.on("beforeload", this.onBeforeLoad, this);
20296 store.on("load", this.onLoad, this);
20297 store.on("loadexception", this.onLoad, this);
20305 * onbeforeLoad - masks the loading area.
20308 onBeforeLoad : function(store,opts)
20310 //Roo.log('onBeforeLoad');
20312 this.el.update("");
20314 this.el.mask(this.mask ? this.mask : "Loading" );
20316 onLoad : function ()
20323 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20324 * @param {HTMLElement} node
20325 * @return {HTMLElement} The template node
20327 findItemFromChild : function(node){
20328 var el = this.dataName ?
20329 this.el.child('.roo-tpl-' + this.dataName,true) :
20332 if(!node || node.parentNode == el){
20335 var p = node.parentNode;
20336 while(p && p != el){
20337 if(p.parentNode == el){
20346 onClick : function(e){
20347 var item = this.findItemFromChild(e.getTarget());
20349 var index = this.indexOf(item);
20350 if(this.onItemClick(item, index, e) !== false){
20351 this.fireEvent("click", this, index, item, e);
20354 this.clearSelections();
20359 onContextMenu : function(e){
20360 var item = this.findItemFromChild(e.getTarget());
20362 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20367 onDblClick : function(e){
20368 var item = this.findItemFromChild(e.getTarget());
20370 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20374 onItemClick : function(item, index, e)
20376 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20379 if (this.toggleSelect) {
20380 var m = this.isSelected(item) ? 'unselect' : 'select';
20383 _t[m](item, true, false);
20386 if(this.multiSelect || this.singleSelect){
20387 if(this.multiSelect && e.shiftKey && this.lastSelection){
20388 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20390 this.select(item, this.multiSelect && e.ctrlKey);
20391 this.lastSelection = item;
20394 if(!this.tickable){
20395 e.preventDefault();
20403 * Get the number of selected nodes.
20406 getSelectionCount : function(){
20407 return this.selections.length;
20411 * Get the currently selected nodes.
20412 * @return {Array} An array of HTMLElements
20414 getSelectedNodes : function(){
20415 return this.selections;
20419 * Get the indexes of the selected nodes.
20422 getSelectedIndexes : function(){
20423 var indexes = [], s = this.selections;
20424 for(var i = 0, len = s.length; i < len; i++){
20425 indexes.push(s[i].nodeIndex);
20431 * Clear all selections
20432 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20434 clearSelections : function(suppressEvent){
20435 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20436 this.cmp.elements = this.selections;
20437 this.cmp.removeClass(this.selectedClass);
20438 this.selections = [];
20439 if(!suppressEvent){
20440 this.fireEvent("selectionchange", this, this.selections);
20446 * Returns true if the passed node is selected
20447 * @param {HTMLElement/Number} node The node or node index
20448 * @return {Boolean}
20450 isSelected : function(node){
20451 var s = this.selections;
20455 node = this.getNode(node);
20456 return s.indexOf(node) !== -1;
20461 * @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
20462 * @param {Boolean} keepExisting (optional) true to keep existing selections
20463 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20465 select : function(nodeInfo, keepExisting, suppressEvent){
20466 if(nodeInfo instanceof Array){
20468 this.clearSelections(true);
20470 for(var i = 0, len = nodeInfo.length; i < len; i++){
20471 this.select(nodeInfo[i], true, true);
20475 var node = this.getNode(nodeInfo);
20476 if(!node || this.isSelected(node)){
20477 return; // already selected.
20480 this.clearSelections(true);
20483 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20484 Roo.fly(node).addClass(this.selectedClass);
20485 this.selections.push(node);
20486 if(!suppressEvent){
20487 this.fireEvent("selectionchange", this, this.selections);
20495 * @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
20496 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20497 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20499 unselect : function(nodeInfo, keepExisting, suppressEvent)
20501 if(nodeInfo instanceof Array){
20502 Roo.each(this.selections, function(s) {
20503 this.unselect(s, nodeInfo);
20507 var node = this.getNode(nodeInfo);
20508 if(!node || !this.isSelected(node)){
20509 //Roo.log("not selected");
20510 return; // not selected.
20514 Roo.each(this.selections, function(s) {
20516 Roo.fly(node).removeClass(this.selectedClass);
20523 this.selections= ns;
20524 this.fireEvent("selectionchange", this, this.selections);
20528 * Gets a template node.
20529 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20530 * @return {HTMLElement} The node or null if it wasn't found
20532 getNode : function(nodeInfo){
20533 if(typeof nodeInfo == "string"){
20534 return document.getElementById(nodeInfo);
20535 }else if(typeof nodeInfo == "number"){
20536 return this.nodes[nodeInfo];
20542 * Gets a range template nodes.
20543 * @param {Number} startIndex
20544 * @param {Number} endIndex
20545 * @return {Array} An array of nodes
20547 getNodes : function(start, end){
20548 var ns = this.nodes;
20549 start = start || 0;
20550 end = typeof end == "undefined" ? ns.length - 1 : end;
20553 for(var i = start; i <= end; i++){
20557 for(var i = start; i >= end; i--){
20565 * Finds the index of the passed node
20566 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20567 * @return {Number} The index of the node or -1
20569 indexOf : function(node){
20570 node = this.getNode(node);
20571 if(typeof node.nodeIndex == "number"){
20572 return node.nodeIndex;
20574 var ns = this.nodes;
20575 for(var i = 0, len = ns.length; i < len; i++){
20586 * based on jquery fullcalendar
20590 Roo.bootstrap = Roo.bootstrap || {};
20592 * @class Roo.bootstrap.Calendar
20593 * @extends Roo.bootstrap.Component
20594 * Bootstrap Calendar class
20595 * @cfg {Boolean} loadMask (true|false) default false
20596 * @cfg {Object} header generate the user specific header of the calendar, default false
20599 * Create a new Container
20600 * @param {Object} config The config object
20605 Roo.bootstrap.Calendar = function(config){
20606 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20610 * Fires when a date is selected
20611 * @param {DatePicker} this
20612 * @param {Date} date The selected date
20616 * @event monthchange
20617 * Fires when the displayed month changes
20618 * @param {DatePicker} this
20619 * @param {Date} date The selected month
20621 'monthchange': true,
20623 * @event evententer
20624 * Fires when mouse over an event
20625 * @param {Calendar} this
20626 * @param {event} Event
20628 'evententer': true,
20630 * @event eventleave
20631 * Fires when the mouse leaves an
20632 * @param {Calendar} this
20635 'eventleave': true,
20637 * @event eventclick
20638 * Fires when the mouse click an
20639 * @param {Calendar} this
20648 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20651 * @cfg {Roo.data.Store} store
20652 * The data source for the calendar
20656 * @cfg {Number} startDay
20657 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20665 getAutoCreate : function(){
20668 var fc_button = function(name, corner, style, content ) {
20669 return Roo.apply({},{
20671 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20673 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20676 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20687 style : 'width:100%',
20694 cls : 'fc-header-left',
20696 fc_button('prev', 'left', 'arrow', '‹' ),
20697 fc_button('next', 'right', 'arrow', '›' ),
20698 { tag: 'span', cls: 'fc-header-space' },
20699 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20707 cls : 'fc-header-center',
20711 cls: 'fc-header-title',
20714 html : 'month / year'
20722 cls : 'fc-header-right',
20724 /* fc_button('month', 'left', '', 'month' ),
20725 fc_button('week', '', '', 'week' ),
20726 fc_button('day', 'right', '', 'day' )
20738 header = this.header;
20741 var cal_heads = function() {
20743 // fixme - handle this.
20745 for (var i =0; i < Date.dayNames.length; i++) {
20746 var d = Date.dayNames[i];
20749 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20750 html : d.substring(0,3)
20754 ret[0].cls += ' fc-first';
20755 ret[6].cls += ' fc-last';
20758 var cal_cell = function(n) {
20761 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20766 cls: 'fc-day-number',
20770 cls: 'fc-day-content',
20774 style: 'position: relative;' // height: 17px;
20786 var cal_rows = function() {
20789 for (var r = 0; r < 6; r++) {
20796 for (var i =0; i < Date.dayNames.length; i++) {
20797 var d = Date.dayNames[i];
20798 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20801 row.cn[0].cls+=' fc-first';
20802 row.cn[0].cn[0].style = 'min-height:90px';
20803 row.cn[6].cls+=' fc-last';
20807 ret[0].cls += ' fc-first';
20808 ret[4].cls += ' fc-prev-last';
20809 ret[5].cls += ' fc-last';
20816 cls: 'fc-border-separate',
20817 style : 'width:100%',
20825 cls : 'fc-first fc-last',
20843 cls : 'fc-content',
20844 style : "position: relative;",
20847 cls : 'fc-view fc-view-month fc-grid',
20848 style : 'position: relative',
20849 unselectable : 'on',
20852 cls : 'fc-event-container',
20853 style : 'position:absolute;z-index:8;top:0;left:0;'
20871 initEvents : function()
20874 throw "can not find store for calendar";
20880 style: "text-align:center",
20884 style: "background-color:white;width:50%;margin:250 auto",
20888 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20899 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20901 var size = this.el.select('.fc-content', true).first().getSize();
20902 this.maskEl.setSize(size.width, size.height);
20903 this.maskEl.enableDisplayMode("block");
20904 if(!this.loadMask){
20905 this.maskEl.hide();
20908 this.store = Roo.factory(this.store, Roo.data);
20909 this.store.on('load', this.onLoad, this);
20910 this.store.on('beforeload', this.onBeforeLoad, this);
20914 this.cells = this.el.select('.fc-day',true);
20915 //Roo.log(this.cells);
20916 this.textNodes = this.el.query('.fc-day-number');
20917 this.cells.addClassOnOver('fc-state-hover');
20919 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20920 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20921 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20922 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20924 this.on('monthchange', this.onMonthChange, this);
20926 this.update(new Date().clearTime());
20929 resize : function() {
20930 var sz = this.el.getSize();
20932 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20933 this.el.select('.fc-day-content div',true).setHeight(34);
20938 showPrevMonth : function(e){
20939 this.update(this.activeDate.add("mo", -1));
20941 showToday : function(e){
20942 this.update(new Date().clearTime());
20945 showNextMonth : function(e){
20946 this.update(this.activeDate.add("mo", 1));
20950 showPrevYear : function(){
20951 this.update(this.activeDate.add("y", -1));
20955 showNextYear : function(){
20956 this.update(this.activeDate.add("y", 1));
20961 update : function(date)
20963 var vd = this.activeDate;
20964 this.activeDate = date;
20965 // if(vd && this.el){
20966 // var t = date.getTime();
20967 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20968 // Roo.log('using add remove');
20970 // this.fireEvent('monthchange', this, date);
20972 // this.cells.removeClass("fc-state-highlight");
20973 // this.cells.each(function(c){
20974 // if(c.dateValue == t){
20975 // c.addClass("fc-state-highlight");
20976 // setTimeout(function(){
20977 // try{c.dom.firstChild.focus();}catch(e){}
20987 var days = date.getDaysInMonth();
20989 var firstOfMonth = date.getFirstDateOfMonth();
20990 var startingPos = firstOfMonth.getDay()-this.startDay;
20992 if(startingPos < this.startDay){
20996 var pm = date.add(Date.MONTH, -1);
20997 var prevStart = pm.getDaysInMonth()-startingPos;
20999 this.cells = this.el.select('.fc-day',true);
21000 this.textNodes = this.el.query('.fc-day-number');
21001 this.cells.addClassOnOver('fc-state-hover');
21003 var cells = this.cells.elements;
21004 var textEls = this.textNodes;
21006 Roo.each(cells, function(cell){
21007 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21010 days += startingPos;
21012 // convert everything to numbers so it's fast
21013 var day = 86400000;
21014 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21017 //Roo.log(prevStart);
21019 var today = new Date().clearTime().getTime();
21020 var sel = date.clearTime().getTime();
21021 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21022 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21023 var ddMatch = this.disabledDatesRE;
21024 var ddText = this.disabledDatesText;
21025 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21026 var ddaysText = this.disabledDaysText;
21027 var format = this.format;
21029 var setCellClass = function(cal, cell){
21033 //Roo.log('set Cell Class');
21035 var t = d.getTime();
21039 cell.dateValue = t;
21041 cell.className += " fc-today";
21042 cell.className += " fc-state-highlight";
21043 cell.title = cal.todayText;
21046 // disable highlight in other month..
21047 //cell.className += " fc-state-highlight";
21052 cell.className = " fc-state-disabled";
21053 cell.title = cal.minText;
21057 cell.className = " fc-state-disabled";
21058 cell.title = cal.maxText;
21062 if(ddays.indexOf(d.getDay()) != -1){
21063 cell.title = ddaysText;
21064 cell.className = " fc-state-disabled";
21067 if(ddMatch && format){
21068 var fvalue = d.dateFormat(format);
21069 if(ddMatch.test(fvalue)){
21070 cell.title = ddText.replace("%0", fvalue);
21071 cell.className = " fc-state-disabled";
21075 if (!cell.initialClassName) {
21076 cell.initialClassName = cell.dom.className;
21079 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21084 for(; i < startingPos; i++) {
21085 textEls[i].innerHTML = (++prevStart);
21086 d.setDate(d.getDate()+1);
21088 cells[i].className = "fc-past fc-other-month";
21089 setCellClass(this, cells[i]);
21094 for(; i < days; i++){
21095 intDay = i - startingPos + 1;
21096 textEls[i].innerHTML = (intDay);
21097 d.setDate(d.getDate()+1);
21099 cells[i].className = ''; // "x-date-active";
21100 setCellClass(this, cells[i]);
21104 for(; i < 42; i++) {
21105 textEls[i].innerHTML = (++extraDays);
21106 d.setDate(d.getDate()+1);
21108 cells[i].className = "fc-future fc-other-month";
21109 setCellClass(this, cells[i]);
21112 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21114 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21116 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21117 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21119 if(totalRows != 6){
21120 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21121 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21124 this.fireEvent('monthchange', this, date);
21128 if(!this.internalRender){
21129 var main = this.el.dom.firstChild;
21130 var w = main.offsetWidth;
21131 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21132 Roo.fly(main).setWidth(w);
21133 this.internalRender = true;
21134 // opera does not respect the auto grow header center column
21135 // then, after it gets a width opera refuses to recalculate
21136 // without a second pass
21137 if(Roo.isOpera && !this.secondPass){
21138 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21139 this.secondPass = true;
21140 this.update.defer(10, this, [date]);
21147 findCell : function(dt) {
21148 dt = dt.clearTime().getTime();
21150 this.cells.each(function(c){
21151 //Roo.log("check " +c.dateValue + '?=' + dt);
21152 if(c.dateValue == dt){
21162 findCells : function(ev) {
21163 var s = ev.start.clone().clearTime().getTime();
21165 var e= ev.end.clone().clearTime().getTime();
21168 this.cells.each(function(c){
21169 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21171 if(c.dateValue > e){
21174 if(c.dateValue < s){
21183 // findBestRow: function(cells)
21187 // for (var i =0 ; i < cells.length;i++) {
21188 // ret = Math.max(cells[i].rows || 0,ret);
21195 addItem : function(ev)
21197 // look for vertical location slot in
21198 var cells = this.findCells(ev);
21200 // ev.row = this.findBestRow(cells);
21202 // work out the location.
21206 for(var i =0; i < cells.length; i++) {
21208 cells[i].row = cells[0].row;
21211 cells[i].row = cells[i].row + 1;
21221 if (crow.start.getY() == cells[i].getY()) {
21223 crow.end = cells[i];
21240 cells[0].events.push(ev);
21242 this.calevents.push(ev);
21245 clearEvents: function() {
21247 if(!this.calevents){
21251 Roo.each(this.cells.elements, function(c){
21257 Roo.each(this.calevents, function(e) {
21258 Roo.each(e.els, function(el) {
21259 el.un('mouseenter' ,this.onEventEnter, this);
21260 el.un('mouseleave' ,this.onEventLeave, this);
21265 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21271 renderEvents: function()
21275 this.cells.each(function(c) {
21284 if(c.row != c.events.length){
21285 r = 4 - (4 - (c.row - c.events.length));
21288 c.events = ev.slice(0, r);
21289 c.more = ev.slice(r);
21291 if(c.more.length && c.more.length == 1){
21292 c.events.push(c.more.pop());
21295 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21299 this.cells.each(function(c) {
21301 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21304 for (var e = 0; e < c.events.length; e++){
21305 var ev = c.events[e];
21306 var rows = ev.rows;
21308 for(var i = 0; i < rows.length; i++) {
21310 // how many rows should it span..
21313 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21314 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21316 unselectable : "on",
21319 cls: 'fc-event-inner',
21323 // cls: 'fc-event-time',
21324 // html : cells.length > 1 ? '' : ev.time
21328 cls: 'fc-event-title',
21329 html : String.format('{0}', ev.title)
21336 cls: 'ui-resizable-handle ui-resizable-e',
21337 html : '  '
21344 cfg.cls += ' fc-event-start';
21346 if ((i+1) == rows.length) {
21347 cfg.cls += ' fc-event-end';
21350 var ctr = _this.el.select('.fc-event-container',true).first();
21351 var cg = ctr.createChild(cfg);
21353 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21354 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21356 var r = (c.more.length) ? 1 : 0;
21357 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21358 cg.setWidth(ebox.right - sbox.x -2);
21360 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21361 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21362 cg.on('click', _this.onEventClick, _this, ev);
21373 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21374 style : 'position: absolute',
21375 unselectable : "on",
21378 cls: 'fc-event-inner',
21382 cls: 'fc-event-title',
21390 cls: 'ui-resizable-handle ui-resizable-e',
21391 html : '  '
21397 var ctr = _this.el.select('.fc-event-container',true).first();
21398 var cg = ctr.createChild(cfg);
21400 var sbox = c.select('.fc-day-content',true).first().getBox();
21401 var ebox = c.select('.fc-day-content',true).first().getBox();
21403 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21404 cg.setWidth(ebox.right - sbox.x -2);
21406 cg.on('click', _this.onMoreEventClick, _this, c.more);
21416 onEventEnter: function (e, el,event,d) {
21417 this.fireEvent('evententer', this, el, event);
21420 onEventLeave: function (e, el,event,d) {
21421 this.fireEvent('eventleave', this, el, event);
21424 onEventClick: function (e, el,event,d) {
21425 this.fireEvent('eventclick', this, el, event);
21428 onMonthChange: function () {
21432 onMoreEventClick: function(e, el, more)
21436 this.calpopover.placement = 'right';
21437 this.calpopover.setTitle('More');
21439 this.calpopover.setContent('');
21441 var ctr = this.calpopover.el.select('.popover-content', true).first();
21443 Roo.each(more, function(m){
21445 cls : 'fc-event-hori fc-event-draggable',
21448 var cg = ctr.createChild(cfg);
21450 cg.on('click', _this.onEventClick, _this, m);
21453 this.calpopover.show(el);
21458 onLoad: function ()
21460 this.calevents = [];
21463 if(this.store.getCount() > 0){
21464 this.store.data.each(function(d){
21467 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21468 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21469 time : d.data.start_time,
21470 title : d.data.title,
21471 description : d.data.description,
21472 venue : d.data.venue
21477 this.renderEvents();
21479 if(this.calevents.length && this.loadMask){
21480 this.maskEl.hide();
21484 onBeforeLoad: function()
21486 this.clearEvents();
21488 this.maskEl.show();
21502 * @class Roo.bootstrap.Popover
21503 * @extends Roo.bootstrap.Component
21505 * @children Roo.bootstrap.Component
21506 * Bootstrap Popover class
21507 * @cfg {String} html contents of the popover (or false to use children..)
21508 * @cfg {String} title of popover (or false to hide)
21509 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21510 * @cfg {String} trigger click || hover (or false to trigger manually)
21511 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21512 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21513 * - if false and it has a 'parent' then it will be automatically added to that element
21514 * - if string - Roo.get will be called
21515 * @cfg {Number} delay - delay before showing
21518 * Create a new Popover
21519 * @param {Object} config The config object
21522 Roo.bootstrap.Popover = function(config){
21523 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21529 * After the popover show
21531 * @param {Roo.bootstrap.Popover} this
21536 * After the popover hide
21538 * @param {Roo.bootstrap.Popover} this
21544 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21549 placement : 'right',
21550 trigger : 'hover', // hover
21556 can_build_overlaid : false,
21558 maskEl : false, // the mask element
21561 alignEl : false, // when show is called with an element - this get's stored.
21563 getChildContainer : function()
21565 return this.contentEl;
21568 getPopoverHeader : function()
21570 this.title = true; // flag not to hide it..
21571 this.headerEl.addClass('p-0');
21572 return this.headerEl
21576 getAutoCreate : function(){
21579 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21580 style: 'display:block',
21586 cls : 'popover-inner ',
21590 cls: 'popover-title popover-header',
21591 html : this.title === false ? '' : this.title
21594 cls : 'popover-content popover-body ' + (this.cls || ''),
21595 html : this.html || ''
21606 * @param {string} the title
21608 setTitle: function(str)
21612 this.headerEl.dom.innerHTML = str;
21617 * @param {string} the body content
21619 setContent: function(str)
21622 if (this.contentEl) {
21623 this.contentEl.dom.innerHTML = str;
21627 // as it get's added to the bottom of the page.
21628 onRender : function(ct, position)
21630 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21635 var cfg = Roo.apply({}, this.getAutoCreate());
21639 cfg.cls += ' ' + this.cls;
21642 cfg.style = this.style;
21644 //Roo.log("adding to ");
21645 this.el = Roo.get(document.body).createChild(cfg, position);
21646 // Roo.log(this.el);
21649 this.contentEl = this.el.select('.popover-content',true).first();
21650 this.headerEl = this.el.select('.popover-title',true).first();
21653 if(typeof(this.items) != 'undefined'){
21654 var items = this.items;
21657 for(var i =0;i < items.length;i++) {
21658 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21662 this.items = nitems;
21664 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21665 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21672 resizeMask : function()
21674 this.maskEl.setSize(
21675 Roo.lib.Dom.getViewWidth(true),
21676 Roo.lib.Dom.getViewHeight(true)
21680 initEvents : function()
21684 Roo.bootstrap.Popover.register(this);
21687 this.arrowEl = this.el.select('.arrow',true).first();
21688 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21689 this.el.enableDisplayMode('block');
21693 if (this.over === false && !this.parent()) {
21696 if (this.triggers === false) {
21701 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21702 var triggers = this.trigger ? this.trigger.split(' ') : [];
21703 Roo.each(triggers, function(trigger) {
21705 if (trigger == 'click') {
21706 on_el.on('click', this.toggle, this);
21707 } else if (trigger != 'manual') {
21708 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21709 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21711 on_el.on(eventIn ,this.enter, this);
21712 on_el.on(eventOut, this.leave, this);
21722 toggle : function () {
21723 this.hoverState == 'in' ? this.leave() : this.enter();
21726 enter : function () {
21728 clearTimeout(this.timeout);
21730 this.hoverState = 'in';
21732 if (!this.delay || !this.delay.show) {
21737 this.timeout = setTimeout(function () {
21738 if (_t.hoverState == 'in') {
21741 }, this.delay.show)
21744 leave : function() {
21745 clearTimeout(this.timeout);
21747 this.hoverState = 'out';
21749 if (!this.delay || !this.delay.hide) {
21754 this.timeout = setTimeout(function () {
21755 if (_t.hoverState == 'out') {
21758 }, this.delay.hide)
21762 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21763 * @param {string} (left|right|top|bottom) position
21765 show : function (on_el, placement)
21767 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21768 on_el = on_el || false; // default to false
21771 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21772 on_el = this.parent().el;
21773 } else if (this.over) {
21774 on_el = Roo.get(this.over);
21779 this.alignEl = Roo.get( on_el );
21782 this.render(document.body);
21788 if (this.title === false) {
21789 this.headerEl.hide();
21794 this.el.dom.style.display = 'block';
21797 if (this.alignEl) {
21798 this.updatePosition(this.placement, true);
21801 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21802 var es = this.el.getSize();
21803 var x = Roo.lib.Dom.getViewWidth()/2;
21804 var y = Roo.lib.Dom.getViewHeight()/2;
21805 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21810 //var arrow = this.el.select('.arrow',true).first();
21811 //arrow.set(align[2],
21813 this.el.addClass('in');
21817 this.hoverState = 'in';
21820 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21821 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21822 this.maskEl.dom.style.display = 'block';
21823 this.maskEl.addClass('show');
21825 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21827 this.fireEvent('show', this);
21831 * fire this manually after loading a grid in the table for example
21832 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21833 * @param {Boolean} try and move it if we cant get right position.
21835 updatePosition : function(placement, try_move)
21837 // allow for calling with no parameters
21838 placement = placement ? placement : this.placement;
21839 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21841 this.el.removeClass([
21842 'fade','top','bottom', 'left', 'right','in',
21843 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21845 this.el.addClass(placement + ' bs-popover-' + placement);
21847 if (!this.alignEl ) {
21851 switch (placement) {
21853 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21854 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21855 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21856 //normal display... or moved up/down.
21857 this.el.setXY(offset);
21858 var xy = this.alignEl.getAnchorXY('tr', false);
21860 this.arrowEl.setXY(xy);
21863 // continue through...
21864 return this.updatePosition('left', false);
21868 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21869 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21870 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21871 //normal display... or moved up/down.
21872 this.el.setXY(offset);
21873 var xy = this.alignEl.getAnchorXY('tl', false);
21874 xy[0]-=10;xy[1]+=5; // << fix me
21875 this.arrowEl.setXY(xy);
21879 return this.updatePosition('right', false);
21882 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21883 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21884 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21885 //normal display... or moved up/down.
21886 this.el.setXY(offset);
21887 var xy = this.alignEl.getAnchorXY('t', false);
21888 xy[1]-=10; // << fix me
21889 this.arrowEl.setXY(xy);
21893 return this.updatePosition('bottom', false);
21896 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21897 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21898 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21899 //normal display... or moved up/down.
21900 this.el.setXY(offset);
21901 var xy = this.alignEl.getAnchorXY('b', false);
21902 xy[1]+=2; // << fix me
21903 this.arrowEl.setXY(xy);
21907 return this.updatePosition('top', false);
21918 this.el.setXY([0,0]);
21919 this.el.removeClass('in');
21921 this.hoverState = null;
21922 this.maskEl.hide(); // always..
21923 this.fireEvent('hide', this);
21929 Roo.apply(Roo.bootstrap.Popover, {
21932 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21933 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21934 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21935 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21940 clickHander : false,
21944 onMouseDown : function(e)
21946 if (this.popups.length && !e.getTarget(".roo-popover")) {
21947 /// what is nothing is showing..
21956 register : function(popup)
21958 if (!Roo.bootstrap.Popover.clickHandler) {
21959 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21961 // hide other popups.
21962 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21963 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21964 this.hideAll(); //<< why?
21965 //this.popups.push(popup);
21967 hideAll : function()
21969 this.popups.forEach(function(p) {
21973 onShow : function() {
21974 Roo.bootstrap.Popover.popups.push(this);
21976 onHide : function() {
21977 Roo.bootstrap.Popover.popups.remove(this);
21983 * Card header - holder for the card header elements.
21988 * @class Roo.bootstrap.PopoverNav
21989 * @extends Roo.bootstrap.nav.Simplebar
21990 * @parent Roo.bootstrap.Popover
21991 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21992 * Bootstrap Popover header navigation class
21993 * FIXME? should this go under nav?
21997 * Create a new Popover Header Navigation
21998 * @param {Object} config The config object
22001 Roo.bootstrap.PopoverNav = function(config){
22002 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22005 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22008 container_method : 'getPopoverHeader'
22026 * @class Roo.bootstrap.Progress
22027 * @extends Roo.bootstrap.Component
22028 * @children Roo.bootstrap.ProgressBar
22029 * Bootstrap Progress class
22030 * @cfg {Boolean} striped striped of the progress bar
22031 * @cfg {Boolean} active animated of the progress bar
22035 * Create a new Progress
22036 * @param {Object} config The config object
22039 Roo.bootstrap.Progress = function(config){
22040 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22043 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22048 getAutoCreate : function(){
22056 cfg.cls += ' progress-striped';
22060 cfg.cls += ' active';
22079 * @class Roo.bootstrap.ProgressBar
22080 * @extends Roo.bootstrap.Component
22081 * Bootstrap ProgressBar class
22082 * @cfg {Number} aria_valuenow aria-value now
22083 * @cfg {Number} aria_valuemin aria-value min
22084 * @cfg {Number} aria_valuemax aria-value max
22085 * @cfg {String} label label for the progress bar
22086 * @cfg {String} panel (success | info | warning | danger )
22087 * @cfg {String} role role of the progress bar
22088 * @cfg {String} sr_only text
22092 * Create a new ProgressBar
22093 * @param {Object} config The config object
22096 Roo.bootstrap.ProgressBar = function(config){
22097 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22100 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22104 aria_valuemax : 100,
22110 getAutoCreate : function()
22115 cls: 'progress-bar',
22116 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22128 cfg.role = this.role;
22131 if(this.aria_valuenow){
22132 cfg['aria-valuenow'] = this.aria_valuenow;
22135 if(this.aria_valuemin){
22136 cfg['aria-valuemin'] = this.aria_valuemin;
22139 if(this.aria_valuemax){
22140 cfg['aria-valuemax'] = this.aria_valuemax;
22143 if(this.label && !this.sr_only){
22144 cfg.html = this.label;
22148 cfg.cls += ' progress-bar-' + this.panel;
22154 update : function(aria_valuenow)
22156 this.aria_valuenow = aria_valuenow;
22158 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22166 * @class Roo.bootstrap.TabGroup
22167 * @extends Roo.bootstrap.Column
22168 * @children Roo.bootstrap.TabPanel
22169 * Bootstrap Column class
22170 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22171 * @cfg {Boolean} carousel true to make the group behave like a carousel
22172 * @cfg {Boolean} bullets show bullets for the panels
22173 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22174 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22175 * @cfg {Boolean} showarrow (true|false) show arrow default true
22178 * Create a new TabGroup
22179 * @param {Object} config The config object
22182 Roo.bootstrap.TabGroup = function(config){
22183 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22185 this.navId = Roo.id();
22188 Roo.bootstrap.TabGroup.register(this);
22192 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22195 transition : false,
22200 slideOnTouch : false,
22203 getAutoCreate : function()
22205 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22207 cfg.cls += ' tab-content';
22209 if (this.carousel) {
22210 cfg.cls += ' carousel slide';
22213 cls : 'carousel-inner',
22217 if(this.bullets && !Roo.isTouch){
22220 cls : 'carousel-bullets',
22224 if(this.bullets_cls){
22225 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22232 cfg.cn[0].cn.push(bullets);
22235 if(this.showarrow){
22236 cfg.cn[0].cn.push({
22238 class : 'carousel-arrow',
22242 class : 'carousel-prev',
22246 class : 'fa fa-chevron-left'
22252 class : 'carousel-next',
22256 class : 'fa fa-chevron-right'
22269 initEvents: function()
22271 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22272 // this.el.on("touchstart", this.onTouchStart, this);
22275 if(this.autoslide){
22278 this.slideFn = window.setInterval(function() {
22279 _this.showPanelNext();
22283 if(this.showarrow){
22284 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22285 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22291 // onTouchStart : function(e, el, o)
22293 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22297 // this.showPanelNext();
22301 getChildContainer : function()
22303 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22307 * register a Navigation item
22308 * @param {Roo.bootstrap.nav.Item} the navitem to add
22310 register : function(item)
22312 this.tabs.push( item);
22313 item.navId = this.navId; // not really needed..
22318 getActivePanel : function()
22321 Roo.each(this.tabs, function(t) {
22331 getPanelByName : function(n)
22334 Roo.each(this.tabs, function(t) {
22335 if (t.tabId == n) {
22343 indexOfPanel : function(p)
22346 Roo.each(this.tabs, function(t,i) {
22347 if (t.tabId == p.tabId) {
22356 * show a specific panel
22357 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22358 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22360 showPanel : function (pan)
22362 if(this.transition || typeof(pan) == 'undefined'){
22363 Roo.log("waiting for the transitionend");
22367 if (typeof(pan) == 'number') {
22368 pan = this.tabs[pan];
22371 if (typeof(pan) == 'string') {
22372 pan = this.getPanelByName(pan);
22375 var cur = this.getActivePanel();
22378 Roo.log('pan or acitve pan is undefined');
22382 if (pan.tabId == this.getActivePanel().tabId) {
22386 if (false === cur.fireEvent('beforedeactivate')) {
22390 if(this.bullets > 0 && !Roo.isTouch){
22391 this.setActiveBullet(this.indexOfPanel(pan));
22394 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22396 //class="carousel-item carousel-item-next carousel-item-left"
22398 this.transition = true;
22399 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22400 var lr = dir == 'next' ? 'left' : 'right';
22401 pan.el.addClass(dir); // or prev
22402 pan.el.addClass('carousel-item-' + dir); // or prev
22403 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22404 cur.el.addClass(lr); // or right
22405 pan.el.addClass(lr);
22406 cur.el.addClass('carousel-item-' +lr); // or right
22407 pan.el.addClass('carousel-item-' +lr);
22411 cur.el.on('transitionend', function() {
22412 Roo.log("trans end?");
22414 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22415 pan.setActive(true);
22417 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22418 cur.setActive(false);
22420 _this.transition = false;
22422 }, this, { single: true } );
22427 cur.setActive(false);
22428 pan.setActive(true);
22433 showPanelNext : function()
22435 var i = this.indexOfPanel(this.getActivePanel());
22437 if (i >= this.tabs.length - 1 && !this.autoslide) {
22441 if (i >= this.tabs.length - 1 && this.autoslide) {
22445 this.showPanel(this.tabs[i+1]);
22448 showPanelPrev : function()
22450 var i = this.indexOfPanel(this.getActivePanel());
22452 if (i < 1 && !this.autoslide) {
22456 if (i < 1 && this.autoslide) {
22457 i = this.tabs.length;
22460 this.showPanel(this.tabs[i-1]);
22464 addBullet: function()
22466 if(!this.bullets || Roo.isTouch){
22469 var ctr = this.el.select('.carousel-bullets',true).first();
22470 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22471 var bullet = ctr.createChild({
22472 cls : 'bullet bullet-' + i
22473 },ctr.dom.lastChild);
22478 bullet.on('click', (function(e, el, o, ii, t){
22480 e.preventDefault();
22482 this.showPanel(ii);
22484 if(this.autoslide && this.slideFn){
22485 clearInterval(this.slideFn);
22486 this.slideFn = window.setInterval(function() {
22487 _this.showPanelNext();
22491 }).createDelegate(this, [i, bullet], true));
22496 setActiveBullet : function(i)
22502 Roo.each(this.el.select('.bullet', true).elements, function(el){
22503 el.removeClass('selected');
22506 var bullet = this.el.select('.bullet-' + i, true).first();
22512 bullet.addClass('selected');
22523 Roo.apply(Roo.bootstrap.TabGroup, {
22527 * register a Navigation Group
22528 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22530 register : function(navgrp)
22532 this.groups[navgrp.navId] = navgrp;
22536 * fetch a Navigation Group based on the navigation ID
22537 * if one does not exist , it will get created.
22538 * @param {string} the navgroup to add
22539 * @returns {Roo.bootstrap.nav.Group} the navgroup
22541 get: function(navId) {
22542 if (typeof(this.groups[navId]) == 'undefined') {
22543 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22545 return this.groups[navId] ;
22560 * @class Roo.bootstrap.TabPanel
22561 * @extends Roo.bootstrap.Component
22562 * @children Roo.bootstrap.Component
22563 * Bootstrap TabPanel class
22564 * @cfg {Boolean} active panel active
22565 * @cfg {String} html panel content
22566 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22567 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22568 * @cfg {String} href click to link..
22569 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22573 * Create a new TabPanel
22574 * @param {Object} config The config object
22577 Roo.bootstrap.TabPanel = function(config){
22578 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22582 * Fires when the active status changes
22583 * @param {Roo.bootstrap.TabPanel} this
22584 * @param {Boolean} state the new state
22589 * @event beforedeactivate
22590 * Fires before a tab is de-activated - can be used to do validation on a form.
22591 * @param {Roo.bootstrap.TabPanel} this
22592 * @return {Boolean} false if there is an error
22595 'beforedeactivate': true
22598 this.tabId = this.tabId || Roo.id();
22602 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22609 touchSlide : false,
22610 getAutoCreate : function(){
22615 // item is needed for carousel - not sure if it has any effect otherwise
22616 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22617 html: this.html || ''
22621 cfg.cls += ' active';
22625 cfg.tabId = this.tabId;
22633 initEvents: function()
22635 var p = this.parent();
22637 this.navId = this.navId || p.navId;
22639 if (typeof(this.navId) != 'undefined') {
22640 // not really needed.. but just in case.. parent should be a NavGroup.
22641 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22645 var i = tg.tabs.length - 1;
22647 if(this.active && tg.bullets > 0 && i < tg.bullets){
22648 tg.setActiveBullet(i);
22652 this.el.on('click', this.onClick, this);
22654 if(Roo.isTouch && this.touchSlide){
22655 this.el.on("touchstart", this.onTouchStart, this);
22656 this.el.on("touchmove", this.onTouchMove, this);
22657 this.el.on("touchend", this.onTouchEnd, this);
22662 onRender : function(ct, position)
22664 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22667 setActive : function(state)
22669 Roo.log("panel - set active " + this.tabId + "=" + state);
22671 this.active = state;
22673 this.el.removeClass('active');
22675 } else if (!this.el.hasClass('active')) {
22676 this.el.addClass('active');
22679 this.fireEvent('changed', this, state);
22682 onClick : function(e)
22684 e.preventDefault();
22686 if(!this.href.length){
22690 window.location.href = this.href;
22699 onTouchStart : function(e)
22701 this.swiping = false;
22703 this.startX = e.browserEvent.touches[0].clientX;
22704 this.startY = e.browserEvent.touches[0].clientY;
22707 onTouchMove : function(e)
22709 this.swiping = true;
22711 this.endX = e.browserEvent.touches[0].clientX;
22712 this.endY = e.browserEvent.touches[0].clientY;
22715 onTouchEnd : function(e)
22722 var tabGroup = this.parent();
22724 if(this.endX > this.startX){ // swiping right
22725 tabGroup.showPanelPrev();
22729 if(this.startX > this.endX){ // swiping left
22730 tabGroup.showPanelNext();
22749 * @class Roo.bootstrap.DateField
22750 * @extends Roo.bootstrap.Input
22751 * Bootstrap DateField class
22752 * @cfg {Number} weekStart default 0
22753 * @cfg {String} viewMode default empty, (months|years)
22754 * @cfg {String} minViewMode default empty, (months|years)
22755 * @cfg {Number} startDate default -Infinity
22756 * @cfg {Number} endDate default Infinity
22757 * @cfg {Boolean} todayHighlight default false
22758 * @cfg {Boolean} todayBtn default false
22759 * @cfg {Boolean} calendarWeeks default false
22760 * @cfg {Object} daysOfWeekDisabled default empty
22761 * @cfg {Boolean} singleMode default false (true | false)
22763 * @cfg {Boolean} keyboardNavigation default true
22764 * @cfg {String} language default en
22767 * Create a new DateField
22768 * @param {Object} config The config object
22771 Roo.bootstrap.DateField = function(config){
22772 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22776 * Fires when this field show.
22777 * @param {Roo.bootstrap.DateField} this
22778 * @param {Mixed} date The date value
22783 * Fires when this field hide.
22784 * @param {Roo.bootstrap.DateField} this
22785 * @param {Mixed} date The date value
22790 * Fires when select a date.
22791 * @param {Roo.bootstrap.DateField} this
22792 * @param {Mixed} date The date value
22796 * @event beforeselect
22797 * Fires when before select a date.
22798 * @param {Roo.bootstrap.DateField} this
22799 * @param {Mixed} date The date value
22801 beforeselect : true
22805 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22808 * @cfg {String} format
22809 * The default date format string which can be overriden for localization support. The format must be
22810 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22814 * @cfg {String} altFormats
22815 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22816 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22818 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22826 todayHighlight : false,
22832 keyboardNavigation: true,
22834 calendarWeeks: false,
22836 startDate: -Infinity,
22840 daysOfWeekDisabled: [],
22844 singleMode : false,
22846 UTCDate: function()
22848 return new Date(Date.UTC.apply(Date, arguments));
22851 UTCToday: function()
22853 var today = new Date();
22854 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22857 getDate: function() {
22858 var d = this.getUTCDate();
22859 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22862 getUTCDate: function() {
22866 setDate: function(d) {
22867 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22870 setUTCDate: function(d) {
22872 this.setValue(this.formatDate(this.date));
22875 onRender: function(ct, position)
22878 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22880 this.language = this.language || 'en';
22881 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22882 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22884 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22885 this.format = this.format || 'm/d/y';
22886 this.isInline = false;
22887 this.isInput = true;
22888 this.component = this.el.select('.add-on', true).first() || false;
22889 this.component = (this.component && this.component.length === 0) ? false : this.component;
22890 this.hasInput = this.component && this.inputEl().length;
22892 if (typeof(this.minViewMode === 'string')) {
22893 switch (this.minViewMode) {
22895 this.minViewMode = 1;
22898 this.minViewMode = 2;
22901 this.minViewMode = 0;
22906 if (typeof(this.viewMode === 'string')) {
22907 switch (this.viewMode) {
22920 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22922 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22924 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22926 this.picker().on('mousedown', this.onMousedown, this);
22927 this.picker().on('click', this.onClick, this);
22929 this.picker().addClass('datepicker-dropdown');
22931 this.startViewMode = this.viewMode;
22933 if(this.singleMode){
22934 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22935 v.setVisibilityMode(Roo.Element.DISPLAY);
22939 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22940 v.setStyle('width', '189px');
22944 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22945 if(!this.calendarWeeks){
22950 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22951 v.attr('colspan', function(i, val){
22952 return parseInt(val) + 1;
22957 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22959 this.setStartDate(this.startDate);
22960 this.setEndDate(this.endDate);
22962 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22969 if(this.isInline) {
22974 picker : function()
22976 return this.pickerEl;
22977 // return this.el.select('.datepicker', true).first();
22980 fillDow: function()
22982 var dowCnt = this.weekStart;
22991 if(this.calendarWeeks){
22999 while (dowCnt < this.weekStart + 7) {
23003 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23007 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23010 fillMonths: function()
23013 var months = this.picker().select('>.datepicker-months td', true).first();
23015 months.dom.innerHTML = '';
23021 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
23024 months.createChild(month);
23031 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;
23033 if (this.date < this.startDate) {
23034 this.viewDate = new Date(this.startDate);
23035 } else if (this.date > this.endDate) {
23036 this.viewDate = new Date(this.endDate);
23038 this.viewDate = new Date(this.date);
23046 var d = new Date(this.viewDate),
23047 year = d.getUTCFullYear(),
23048 month = d.getUTCMonth(),
23049 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23050 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23051 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23052 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23053 currentDate = this.date && this.date.valueOf(),
23054 today = this.UTCToday();
23056 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
23058 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
23060 // this.picker.select('>tfoot th.today').
23061 // .text(dates[this.language].today)
23062 // .toggle(this.todayBtn !== false);
23064 this.updateNavArrows();
23067 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23069 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23071 prevMonth.setUTCDate(day);
23073 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23075 var nextMonth = new Date(prevMonth);
23077 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23079 nextMonth = nextMonth.valueOf();
23081 var fillMonths = false;
23083 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23085 while(prevMonth.valueOf() <= nextMonth) {
23088 if (prevMonth.getUTCDay() === this.weekStart) {
23090 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23098 if(this.calendarWeeks){
23099 // ISO 8601: First week contains first thursday.
23100 // ISO also states week starts on Monday, but we can be more abstract here.
23102 // Start of current week: based on weekstart/current date
23103 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23104 // Thursday of this week
23105 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23106 // First Thursday of year, year from thursday
23107 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23108 // Calendar week: ms between thursdays, div ms per day, div 7 days
23109 calWeek = (th - yth) / 864e5 / 7 + 1;
23111 fillMonths.cn.push({
23119 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23121 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23124 if (this.todayHighlight &&
23125 prevMonth.getUTCFullYear() == today.getFullYear() &&
23126 prevMonth.getUTCMonth() == today.getMonth() &&
23127 prevMonth.getUTCDate() == today.getDate()) {
23128 clsName += ' today';
23131 if (currentDate && prevMonth.valueOf() === currentDate) {
23132 clsName += ' active';
23135 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23136 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23137 clsName += ' disabled';
23140 fillMonths.cn.push({
23142 cls: 'day ' + clsName,
23143 html: prevMonth.getDate()
23146 prevMonth.setDate(prevMonth.getDate()+1);
23149 var currentYear = this.date && this.date.getUTCFullYear();
23150 var currentMonth = this.date && this.date.getUTCMonth();
23152 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23154 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23155 v.removeClass('active');
23157 if(currentYear === year && k === currentMonth){
23158 v.addClass('active');
23161 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23162 v.addClass('disabled');
23168 year = parseInt(year/10, 10) * 10;
23170 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23172 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23175 for (var i = -1; i < 11; i++) {
23176 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23178 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23186 showMode: function(dir)
23189 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23192 Roo.each(this.picker().select('>div',true).elements, function(v){
23193 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23196 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
23201 if(this.isInline) {
23205 this.picker().removeClass(['bottom', 'top']);
23207 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23209 * place to the top of element!
23213 this.picker().addClass('top');
23214 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23219 this.picker().addClass('bottom');
23221 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23224 parseDate : function(value)
23226 if(!value || value instanceof Date){
23229 var v = Date.parseDate(value, this.format);
23230 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23231 v = Date.parseDate(value, 'Y-m-d');
23233 if(!v && this.altFormats){
23234 if(!this.altFormatsArray){
23235 this.altFormatsArray = this.altFormats.split("|");
23237 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23238 v = Date.parseDate(value, this.altFormatsArray[i]);
23244 formatDate : function(date, fmt)
23246 return (!date || !(date instanceof Date)) ?
23247 date : date.dateFormat(fmt || this.format);
23250 onFocus : function()
23252 Roo.bootstrap.DateField.superclass.onFocus.call(this);
23256 onBlur : function()
23258 Roo.bootstrap.DateField.superclass.onBlur.call(this);
23260 var d = this.inputEl().getValue();
23267 showPopup : function()
23269 this.picker().show();
23273 this.fireEvent('showpopup', this, this.date);
23276 hidePopup : function()
23278 if(this.isInline) {
23281 this.picker().hide();
23282 this.viewMode = this.startViewMode;
23285 this.fireEvent('hidepopup', this, this.date);
23289 onMousedown: function(e)
23291 e.stopPropagation();
23292 e.preventDefault();
23297 Roo.bootstrap.DateField.superclass.keyup.call(this);
23301 setValue: function(v)
23303 if(this.fireEvent('beforeselect', this, v) !== false){
23304 var d = new Date(this.parseDate(v) ).clearTime();
23306 if(isNaN(d.getTime())){
23307 this.date = this.viewDate = '';
23308 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23312 v = this.formatDate(d);
23314 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
23316 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23320 this.fireEvent('select', this, this.date);
23324 getValue: function()
23326 return this.formatDate(this.date);
23329 fireKey: function(e)
23331 if (!this.picker().isVisible()){
23332 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23338 var dateChanged = false,
23340 newDate, newViewDate;
23345 e.preventDefault();
23349 if (!this.keyboardNavigation) {
23352 dir = e.keyCode == 37 ? -1 : 1;
23355 newDate = this.moveYear(this.date, dir);
23356 newViewDate = this.moveYear(this.viewDate, dir);
23357 } else if (e.shiftKey){
23358 newDate = this.moveMonth(this.date, dir);
23359 newViewDate = this.moveMonth(this.viewDate, dir);
23361 newDate = new Date(this.date);
23362 newDate.setUTCDate(this.date.getUTCDate() + dir);
23363 newViewDate = new Date(this.viewDate);
23364 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23366 if (this.dateWithinRange(newDate)){
23367 this.date = newDate;
23368 this.viewDate = newViewDate;
23369 this.setValue(this.formatDate(this.date));
23371 e.preventDefault();
23372 dateChanged = true;
23377 if (!this.keyboardNavigation) {
23380 dir = e.keyCode == 38 ? -1 : 1;
23382 newDate = this.moveYear(this.date, dir);
23383 newViewDate = this.moveYear(this.viewDate, dir);
23384 } else if (e.shiftKey){
23385 newDate = this.moveMonth(this.date, dir);
23386 newViewDate = this.moveMonth(this.viewDate, dir);
23388 newDate = new Date(this.date);
23389 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23390 newViewDate = new Date(this.viewDate);
23391 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23393 if (this.dateWithinRange(newDate)){
23394 this.date = newDate;
23395 this.viewDate = newViewDate;
23396 this.setValue(this.formatDate(this.date));
23398 e.preventDefault();
23399 dateChanged = true;
23403 this.setValue(this.formatDate(this.date));
23405 e.preventDefault();
23408 this.setValue(this.formatDate(this.date));
23422 onClick: function(e)
23424 e.stopPropagation();
23425 e.preventDefault();
23427 var target = e.getTarget();
23429 if(target.nodeName.toLowerCase() === 'i'){
23430 target = Roo.get(target).dom.parentNode;
23433 var nodeName = target.nodeName;
23434 var className = target.className;
23435 var html = target.innerHTML;
23436 //Roo.log(nodeName);
23438 switch(nodeName.toLowerCase()) {
23440 switch(className) {
23446 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23447 switch(this.viewMode){
23449 this.viewDate = this.moveMonth(this.viewDate, dir);
23453 this.viewDate = this.moveYear(this.viewDate, dir);
23459 var date = new Date();
23460 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23462 this.setValue(this.formatDate(this.date));
23469 if (className.indexOf('disabled') < 0) {
23470 if (!this.viewDate) {
23471 this.viewDate = new Date();
23473 this.viewDate.setUTCDate(1);
23474 if (className.indexOf('month') > -1) {
23475 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23477 var year = parseInt(html, 10) || 0;
23478 this.viewDate.setUTCFullYear(year);
23482 if(this.singleMode){
23483 this.setValue(this.formatDate(this.viewDate));
23494 //Roo.log(className);
23495 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23496 var day = parseInt(html, 10) || 1;
23497 var year = (this.viewDate || new Date()).getUTCFullYear(),
23498 month = (this.viewDate || new Date()).getUTCMonth();
23500 if (className.indexOf('old') > -1) {
23507 } else if (className.indexOf('new') > -1) {
23515 //Roo.log([year,month,day]);
23516 this.date = this.UTCDate(year, month, day,0,0,0,0);
23517 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23519 //Roo.log(this.formatDate(this.date));
23520 this.setValue(this.formatDate(this.date));
23527 setStartDate: function(startDate)
23529 this.startDate = startDate || -Infinity;
23530 if (this.startDate !== -Infinity) {
23531 this.startDate = this.parseDate(this.startDate);
23534 this.updateNavArrows();
23537 setEndDate: function(endDate)
23539 this.endDate = endDate || Infinity;
23540 if (this.endDate !== Infinity) {
23541 this.endDate = this.parseDate(this.endDate);
23544 this.updateNavArrows();
23547 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23549 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23550 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23551 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23553 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23554 return parseInt(d, 10);
23557 this.updateNavArrows();
23560 updateNavArrows: function()
23562 if(this.singleMode){
23566 var d = new Date(this.viewDate),
23567 year = d.getUTCFullYear(),
23568 month = d.getUTCMonth();
23570 Roo.each(this.picker().select('.prev', true).elements, function(v){
23572 switch (this.viewMode) {
23575 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23581 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23588 Roo.each(this.picker().select('.next', true).elements, function(v){
23590 switch (this.viewMode) {
23593 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23599 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23607 moveMonth: function(date, dir)
23612 var new_date = new Date(date.valueOf()),
23613 day = new_date.getUTCDate(),
23614 month = new_date.getUTCMonth(),
23615 mag = Math.abs(dir),
23617 dir = dir > 0 ? 1 : -1;
23620 // If going back one month, make sure month is not current month
23621 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23623 return new_date.getUTCMonth() == month;
23625 // If going forward one month, make sure month is as expected
23626 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23628 return new_date.getUTCMonth() != new_month;
23630 new_month = month + dir;
23631 new_date.setUTCMonth(new_month);
23632 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23633 if (new_month < 0 || new_month > 11) {
23634 new_month = (new_month + 12) % 12;
23637 // For magnitudes >1, move one month at a time...
23638 for (var i=0; i<mag; i++) {
23639 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23640 new_date = this.moveMonth(new_date, dir);
23642 // ...then reset the day, keeping it in the new month
23643 new_month = new_date.getUTCMonth();
23644 new_date.setUTCDate(day);
23646 return new_month != new_date.getUTCMonth();
23649 // Common date-resetting loop -- if date is beyond end of month, make it
23652 new_date.setUTCDate(--day);
23653 new_date.setUTCMonth(new_month);
23658 moveYear: function(date, dir)
23660 return this.moveMonth(date, dir*12);
23663 dateWithinRange: function(date)
23665 return date >= this.startDate && date <= this.endDate;
23671 this.picker().remove();
23674 validateValue : function(value)
23676 if(this.getVisibilityEl().hasClass('hidden')){
23680 if(value.length < 1) {
23681 if(this.allowBlank){
23687 if(value.length < this.minLength){
23690 if(value.length > this.maxLength){
23694 var vt = Roo.form.VTypes;
23695 if(!vt[this.vtype](value, this)){
23699 if(typeof this.validator == "function"){
23700 var msg = this.validator(value);
23706 if(this.regex && !this.regex.test(value)){
23710 if(typeof(this.parseDate(value)) == 'undefined'){
23714 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23718 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23728 this.date = this.viewDate = '';
23730 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23735 Roo.apply(Roo.bootstrap.DateField, {
23746 html: '<i class="fa fa-arrow-left"/>'
23756 html: '<i class="fa fa-arrow-right"/>'
23798 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23799 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23800 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23801 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23802 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23815 navFnc: 'FullYear',
23820 navFnc: 'FullYear',
23825 Roo.apply(Roo.bootstrap.DateField, {
23829 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23833 cls: 'datepicker-days',
23837 cls: 'table-condensed',
23839 Roo.bootstrap.DateField.head,
23843 Roo.bootstrap.DateField.footer
23850 cls: 'datepicker-months',
23854 cls: 'table-condensed',
23856 Roo.bootstrap.DateField.head,
23857 Roo.bootstrap.DateField.content,
23858 Roo.bootstrap.DateField.footer
23865 cls: 'datepicker-years',
23869 cls: 'table-condensed',
23871 Roo.bootstrap.DateField.head,
23872 Roo.bootstrap.DateField.content,
23873 Roo.bootstrap.DateField.footer
23892 * @class Roo.bootstrap.TimeField
23893 * @extends Roo.bootstrap.Input
23894 * Bootstrap DateField class
23898 * Create a new TimeField
23899 * @param {Object} config The config object
23902 Roo.bootstrap.TimeField = function(config){
23903 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23907 * Fires when this field show.
23908 * @param {Roo.bootstrap.DateField} thisthis
23909 * @param {Mixed} date The date value
23914 * Fires when this field hide.
23915 * @param {Roo.bootstrap.DateField} this
23916 * @param {Mixed} date The date value
23921 * Fires when select a date.
23922 * @param {Roo.bootstrap.DateField} this
23923 * @param {Mixed} date The date value
23929 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23932 * @cfg {String} format
23933 * The default time format string which can be overriden for localization support. The format must be
23934 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23938 getAutoCreate : function()
23940 this.after = '<i class="fa far fa-clock"></i>';
23941 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23945 onRender: function(ct, position)
23948 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23950 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23952 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23954 this.pop = this.picker().select('>.datepicker-time',true).first();
23955 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23957 this.picker().on('mousedown', this.onMousedown, this);
23958 this.picker().on('click', this.onClick, this);
23960 this.picker().addClass('datepicker-dropdown');
23965 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23966 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23967 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23968 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23969 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23970 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23974 fireKey: function(e){
23975 if (!this.picker().isVisible()){
23976 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23982 e.preventDefault();
23990 this.onTogglePeriod();
23993 this.onIncrementMinutes();
23996 this.onDecrementMinutes();
24005 onClick: function(e) {
24006 e.stopPropagation();
24007 e.preventDefault();
24010 picker : function()
24012 return this.pickerEl;
24015 fillTime: function()
24017 var time = this.pop.select('tbody', true).first();
24019 time.dom.innerHTML = '';
24034 cls: 'hours-up fa fas fa-chevron-up'
24054 cls: 'minutes-up fa fas fa-chevron-up'
24075 cls: 'timepicker-hour',
24090 cls: 'timepicker-minute',
24105 cls: 'btn btn-primary period',
24127 cls: 'hours-down fa fas fa-chevron-down'
24147 cls: 'minutes-down fa fas fa-chevron-down'
24165 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24172 var hours = this.time.getHours();
24173 var minutes = this.time.getMinutes();
24186 hours = hours - 12;
24190 hours = '0' + hours;
24194 minutes = '0' + minutes;
24197 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24198 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24199 this.pop.select('button', true).first().dom.innerHTML = period;
24205 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24207 var cls = ['bottom'];
24209 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24216 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24220 //this.picker().setXY(20000,20000);
24221 this.picker().addClass(cls.join('-'));
24225 Roo.each(cls, function(c){
24230 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24231 //_this.picker().setTop(_this.inputEl().getHeight());
24235 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24237 //_this.picker().setTop(0 - _this.picker().getHeight());
24242 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24246 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24254 onFocus : function()
24256 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
24260 onBlur : function()
24262 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
24268 this.picker().show();
24273 this.fireEvent('show', this, this.date);
24278 this.picker().hide();
24281 this.fireEvent('hide', this, this.date);
24284 setTime : function()
24287 this.setValue(this.time.format(this.format));
24289 this.fireEvent('select', this, this.date);
24294 onMousedown: function(e){
24295 e.stopPropagation();
24296 e.preventDefault();
24299 onIncrementHours: function()
24301 Roo.log('onIncrementHours');
24302 this.time = this.time.add(Date.HOUR, 1);
24307 onDecrementHours: function()
24309 Roo.log('onDecrementHours');
24310 this.time = this.time.add(Date.HOUR, -1);
24314 onIncrementMinutes: function()
24316 Roo.log('onIncrementMinutes');
24317 this.time = this.time.add(Date.MINUTE, 1);
24321 onDecrementMinutes: function()
24323 Roo.log('onDecrementMinutes');
24324 this.time = this.time.add(Date.MINUTE, -1);
24328 onTogglePeriod: function()
24330 Roo.log('onTogglePeriod');
24331 this.time = this.time.add(Date.HOUR, 12);
24339 Roo.apply(Roo.bootstrap.TimeField, {
24343 cls: 'datepicker dropdown-menu',
24347 cls: 'datepicker-time',
24351 cls: 'table-condensed',
24380 cls: 'btn btn-info ok',
24408 * @class Roo.bootstrap.MonthField
24409 * @extends Roo.bootstrap.Input
24410 * Bootstrap MonthField class
24412 * @cfg {String} language default en
24415 * Create a new MonthField
24416 * @param {Object} config The config object
24419 Roo.bootstrap.MonthField = function(config){
24420 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
24425 * Fires when this field show.
24426 * @param {Roo.bootstrap.MonthField} this
24427 * @param {Mixed} date The date value
24432 * Fires when this field hide.
24433 * @param {Roo.bootstrap.MonthField} this
24434 * @param {Mixed} date The date value
24439 * Fires when select a date.
24440 * @param {Roo.bootstrap.MonthField} this
24441 * @param {String} oldvalue The old value
24442 * @param {String} newvalue The new value
24448 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24450 onRender: function(ct, position)
24453 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24455 this.language = this.language || 'en';
24456 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24457 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24459 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24460 this.isInline = false;
24461 this.isInput = true;
24462 this.component = this.el.select('.add-on', true).first() || false;
24463 this.component = (this.component && this.component.length === 0) ? false : this.component;
24464 this.hasInput = this.component && this.inputEL().length;
24466 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24468 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24470 this.picker().on('mousedown', this.onMousedown, this);
24471 this.picker().on('click', this.onClick, this);
24473 this.picker().addClass('datepicker-dropdown');
24475 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24476 v.setStyle('width', '189px');
24483 if(this.isInline) {
24489 setValue: function(v, suppressEvent)
24491 var o = this.getValue();
24493 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24497 if(suppressEvent !== true){
24498 this.fireEvent('select', this, o, v);
24503 getValue: function()
24508 onClick: function(e)
24510 e.stopPropagation();
24511 e.preventDefault();
24513 var target = e.getTarget();
24515 if(target.nodeName.toLowerCase() === 'i'){
24516 target = Roo.get(target).dom.parentNode;
24519 var nodeName = target.nodeName;
24520 var className = target.className;
24521 var html = target.innerHTML;
24523 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24527 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24529 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24535 picker : function()
24537 return this.pickerEl;
24540 fillMonths: function()
24543 var months = this.picker().select('>.datepicker-months td', true).first();
24545 months.dom.innerHTML = '';
24551 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24554 months.createChild(month);
24563 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24564 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24567 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24568 e.removeClass('active');
24570 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24571 e.addClass('active');
24578 if(this.isInline) {
24582 this.picker().removeClass(['bottom', 'top']);
24584 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24586 * place to the top of element!
24590 this.picker().addClass('top');
24591 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24596 this.picker().addClass('bottom');
24598 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24601 onFocus : function()
24603 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24607 onBlur : function()
24609 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24611 var d = this.inputEl().getValue();
24620 this.picker().show();
24621 this.picker().select('>.datepicker-months', true).first().show();
24625 this.fireEvent('show', this, this.date);
24630 if(this.isInline) {
24633 this.picker().hide();
24634 this.fireEvent('hide', this, this.date);
24638 onMousedown: function(e)
24640 e.stopPropagation();
24641 e.preventDefault();
24646 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24650 fireKey: function(e)
24652 if (!this.picker().isVisible()){
24653 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24664 e.preventDefault();
24668 dir = e.keyCode == 37 ? -1 : 1;
24670 this.vIndex = this.vIndex + dir;
24672 if(this.vIndex < 0){
24676 if(this.vIndex > 11){
24680 if(isNaN(this.vIndex)){
24684 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24690 dir = e.keyCode == 38 ? -1 : 1;
24692 this.vIndex = this.vIndex + dir * 4;
24694 if(this.vIndex < 0){
24698 if(this.vIndex > 11){
24702 if(isNaN(this.vIndex)){
24706 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24711 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24712 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24716 e.preventDefault();
24719 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24720 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24736 this.picker().remove();
24741 Roo.apply(Roo.bootstrap.MonthField, {
24760 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24761 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24766 Roo.apply(Roo.bootstrap.MonthField, {
24770 cls: 'datepicker dropdown-menu roo-dynamic',
24774 cls: 'datepicker-months',
24778 cls: 'table-condensed',
24780 Roo.bootstrap.DateField.content
24800 * @class Roo.bootstrap.CheckBox
24801 * @extends Roo.bootstrap.Input
24802 * Bootstrap CheckBox class
24804 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24805 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24806 * @cfg {String} boxLabel The text that appears beside the checkbox
24807 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24808 * @cfg {Boolean} checked initnal the element
24809 * @cfg {Boolean} inline inline the element (default false)
24810 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24811 * @cfg {String} tooltip label tooltip
24814 * Create a new CheckBox
24815 * @param {Object} config The config object
24818 Roo.bootstrap.CheckBox = function(config){
24819 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24824 * Fires when the element is checked or unchecked.
24825 * @param {Roo.bootstrap.CheckBox} this This input
24826 * @param {Boolean} checked The new checked value
24831 * Fires when the element is click.
24832 * @param {Roo.bootstrap.CheckBox} this This input
24839 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24841 inputType: 'checkbox',
24850 // checkbox success does not make any sense really..
24855 getAutoCreate : function()
24857 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24863 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24866 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24872 type : this.inputType,
24873 value : this.inputValue,
24874 cls : 'roo-' + this.inputType, //'form-box',
24875 placeholder : this.placeholder || ''
24879 if(this.inputType != 'radio'){
24883 cls : 'roo-hidden-value',
24884 value : this.checked ? this.inputValue : this.valueOff
24889 if (this.weight) { // Validity check?
24890 cfg.cls += " " + this.inputType + "-" + this.weight;
24893 if (this.disabled) {
24894 input.disabled=true;
24898 input.checked = this.checked;
24903 input.name = this.name;
24905 if(this.inputType != 'radio'){
24906 hidden.name = this.name;
24907 input.name = '_hidden_' + this.name;
24912 input.cls += ' input-' + this.size;
24917 ['xs','sm','md','lg'].map(function(size){
24918 if (settings[size]) {
24919 cfg.cls += ' col-' + size + '-' + settings[size];
24923 var inputblock = input;
24925 if (this.before || this.after) {
24928 cls : 'input-group',
24933 inputblock.cn.push({
24935 cls : 'input-group-addon',
24940 inputblock.cn.push(input);
24942 if(this.inputType != 'radio'){
24943 inputblock.cn.push(hidden);
24947 inputblock.cn.push({
24949 cls : 'input-group-addon',
24955 var boxLabelCfg = false;
24961 //'for': id, // box label is handled by onclick - so no for...
24963 html: this.boxLabel
24966 boxLabelCfg.tooltip = this.tooltip;
24972 if (align ==='left' && this.fieldLabel.length) {
24973 // Roo.log("left and has label");
24978 cls : 'control-label',
24979 html : this.fieldLabel
24990 cfg.cn[1].cn.push(boxLabelCfg);
24993 if(this.labelWidth > 12){
24994 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24997 if(this.labelWidth < 13 && this.labelmd == 0){
24998 this.labelmd = this.labelWidth;
25001 if(this.labellg > 0){
25002 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25003 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25006 if(this.labelmd > 0){
25007 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25008 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25011 if(this.labelsm > 0){
25012 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25013 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25016 if(this.labelxs > 0){
25017 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25018 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25021 } else if ( this.fieldLabel.length) {
25022 // Roo.log(" label");
25026 tag: this.boxLabel ? 'span' : 'label',
25028 cls: 'control-label box-input-label',
25029 //cls : 'input-group-addon',
25030 html : this.fieldLabel
25037 cfg.cn.push(boxLabelCfg);
25042 // Roo.log(" no label && no align");
25043 cfg.cn = [ inputblock ] ;
25045 cfg.cn.push(boxLabelCfg);
25053 if(this.inputType != 'radio'){
25054 cfg.cn.push(hidden);
25062 * return the real input element.
25064 inputEl: function ()
25066 return this.el.select('input.roo-' + this.inputType,true).first();
25068 hiddenEl: function ()
25070 return this.el.select('input.roo-hidden-value',true).first();
25073 labelEl: function()
25075 return this.el.select('label.control-label',true).first();
25077 /* depricated... */
25081 return this.labelEl();
25084 boxLabelEl: function()
25086 return this.el.select('label.box-label',true).first();
25089 initEvents : function()
25091 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
25093 this.inputEl().on('click', this.onClick, this);
25095 if (this.boxLabel) {
25096 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25099 this.startValue = this.getValue();
25102 Roo.bootstrap.CheckBox.register(this);
25106 onClick : function(e)
25108 if(this.fireEvent('click', this, e) !== false){
25109 this.setChecked(!this.checked);
25114 setChecked : function(state,suppressEvent)
25116 this.startValue = this.getValue();
25118 if(this.inputType == 'radio'){
25120 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25121 e.dom.checked = false;
25124 this.inputEl().dom.checked = true;
25126 this.inputEl().dom.value = this.inputValue;
25128 if(suppressEvent !== true){
25129 this.fireEvent('check', this, true);
25137 this.checked = state;
25139 this.inputEl().dom.checked = state;
25142 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25144 if(suppressEvent !== true){
25145 this.fireEvent('check', this, state);
25151 getValue : function()
25153 if(this.inputType == 'radio'){
25154 return this.getGroupValue();
25157 return this.hiddenEl().dom.value;
25161 getGroupValue : function()
25163 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25167 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25170 setValue : function(v,suppressEvent)
25172 if(this.inputType == 'radio'){
25173 this.setGroupValue(v, suppressEvent);
25177 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25182 setGroupValue : function(v, suppressEvent)
25184 this.startValue = this.getValue();
25186 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25187 e.dom.checked = false;
25189 if(e.dom.value == v){
25190 e.dom.checked = true;
25194 if(suppressEvent !== true){
25195 this.fireEvent('check', this, true);
25203 validate : function()
25205 if(this.getVisibilityEl().hasClass('hidden')){
25211 (this.inputType == 'radio' && this.validateRadio()) ||
25212 (this.inputType == 'checkbox' && this.validateCheckbox())
25218 this.markInvalid();
25222 validateRadio : function()
25224 if(this.getVisibilityEl().hasClass('hidden')){
25228 if(this.allowBlank){
25234 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25235 if(!e.dom.checked){
25247 validateCheckbox : function()
25250 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25251 //return (this.getValue() == this.inputValue) ? true : false;
25254 var group = Roo.bootstrap.CheckBox.get(this.groupId);
25262 for(var i in group){
25263 if(group[i].el.isVisible(true)){
25271 for(var i in group){
25276 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25283 * Mark this field as valid
25285 markValid : function()
25289 this.fireEvent('valid', this);
25291 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25294 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
25301 if(this.inputType == 'radio'){
25302 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25303 var fg = e.findParent('.form-group', false, true);
25304 if (Roo.bootstrap.version == 3) {
25305 fg.removeClass([_this.invalidClass, _this.validClass]);
25306 fg.addClass(_this.validClass);
25308 fg.removeClass(['is-valid', 'is-invalid']);
25309 fg.addClass('is-valid');
25317 var fg = this.el.findParent('.form-group', false, true);
25318 if (Roo.bootstrap.version == 3) {
25319 fg.removeClass([this.invalidClass, this.validClass]);
25320 fg.addClass(this.validClass);
25322 fg.removeClass(['is-valid', 'is-invalid']);
25323 fg.addClass('is-valid');
25328 var group = Roo.bootstrap.CheckBox.get(this.groupId);
25334 for(var i in group){
25335 var fg = group[i].el.findParent('.form-group', false, true);
25336 if (Roo.bootstrap.version == 3) {
25337 fg.removeClass([this.invalidClass, this.validClass]);
25338 fg.addClass(this.validClass);
25340 fg.removeClass(['is-valid', 'is-invalid']);
25341 fg.addClass('is-valid');
25347 * Mark this field as invalid
25348 * @param {String} msg The validation message
25350 markInvalid : function(msg)
25352 if(this.allowBlank){
25358 this.fireEvent('invalid', this, msg);
25360 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25363 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
25367 label.markInvalid();
25370 if(this.inputType == 'radio'){
25372 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25373 var fg = e.findParent('.form-group', false, true);
25374 if (Roo.bootstrap.version == 3) {
25375 fg.removeClass([_this.invalidClass, _this.validClass]);
25376 fg.addClass(_this.invalidClass);
25378 fg.removeClass(['is-invalid', 'is-valid']);
25379 fg.addClass('is-invalid');
25387 var fg = this.el.findParent('.form-group', false, true);
25388 if (Roo.bootstrap.version == 3) {
25389 fg.removeClass([_this.invalidClass, _this.validClass]);
25390 fg.addClass(_this.invalidClass);
25392 fg.removeClass(['is-invalid', 'is-valid']);
25393 fg.addClass('is-invalid');
25398 var group = Roo.bootstrap.CheckBox.get(this.groupId);
25404 for(var i in group){
25405 var fg = group[i].el.findParent('.form-group', false, true);
25406 if (Roo.bootstrap.version == 3) {
25407 fg.removeClass([_this.invalidClass, _this.validClass]);
25408 fg.addClass(_this.invalidClass);
25410 fg.removeClass(['is-invalid', 'is-valid']);
25411 fg.addClass('is-invalid');
25417 clearInvalid : function()
25419 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
25421 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25423 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25425 if (label && label.iconEl) {
25426 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25427 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25431 disable : function()
25433 if(this.inputType != 'radio'){
25434 Roo.bootstrap.CheckBox.superclass.disable.call(this);
25441 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25442 _this.getActionEl().addClass(this.disabledClass);
25443 e.dom.disabled = true;
25447 this.disabled = true;
25448 this.fireEvent("disable", this);
25452 enable : function()
25454 if(this.inputType != 'radio'){
25455 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25462 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25463 _this.getActionEl().removeClass(this.disabledClass);
25464 e.dom.disabled = false;
25468 this.disabled = false;
25469 this.fireEvent("enable", this);
25473 setBoxLabel : function(v)
25478 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25484 Roo.apply(Roo.bootstrap.CheckBox, {
25489 * register a CheckBox Group
25490 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25492 register : function(checkbox)
25494 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25495 this.groups[checkbox.groupId] = {};
25498 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25502 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25506 * fetch a CheckBox Group based on the group ID
25507 * @param {string} the group ID
25508 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25510 get: function(groupId) {
25511 if (typeof(this.groups[groupId]) == 'undefined') {
25515 return this.groups[groupId] ;
25528 * @class Roo.bootstrap.Radio
25529 * @extends Roo.bootstrap.Component
25530 * Bootstrap Radio class
25531 * @cfg {String} boxLabel - the label associated
25532 * @cfg {String} value - the value of radio
25535 * Create a new Radio
25536 * @param {Object} config The config object
25538 Roo.bootstrap.Radio = function(config){
25539 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25543 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25549 getAutoCreate : function()
25553 cls : 'form-group radio',
25558 html : this.boxLabel
25566 initEvents : function()
25568 this.parent().register(this);
25570 this.el.on('click', this.onClick, this);
25574 onClick : function(e)
25576 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25577 this.setChecked(true);
25581 setChecked : function(state, suppressEvent)
25583 this.parent().setValue(this.value, suppressEvent);
25587 setBoxLabel : function(v)
25592 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25607 * @class Roo.bootstrap.SecurePass
25608 * @extends Roo.bootstrap.Input
25609 * Bootstrap SecurePass class
25613 * Create a new SecurePass
25614 * @param {Object} config The config object
25617 Roo.bootstrap.SecurePass = function (config) {
25618 // these go here, so the translation tool can replace them..
25620 PwdEmpty: "Please type a password, and then retype it to confirm.",
25621 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25622 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25623 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25624 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25625 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25626 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25627 TooWeak: "Your password is Too Weak."
25629 this.meterLabel = "Password strength:";
25630 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25631 this.meterClass = [
25632 "roo-password-meter-tooweak",
25633 "roo-password-meter-weak",
25634 "roo-password-meter-medium",
25635 "roo-password-meter-strong",
25636 "roo-password-meter-grey"
25641 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25644 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25646 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25648 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25649 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25650 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25651 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25652 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25653 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25654 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25664 * @cfg {String/Object} Label for the strength meter (defaults to
25665 * 'Password strength:')
25670 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25671 * ['Weak', 'Medium', 'Strong'])
25674 pwdStrengths: false,
25687 initEvents: function ()
25689 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25691 if (this.el.is('input[type=password]') && Roo.isSafari) {
25692 this.el.on('keydown', this.SafariOnKeyDown, this);
25695 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25698 onRender: function (ct, position)
25700 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25701 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25702 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25704 this.trigger.createChild({
25709 cls: 'roo-password-meter-grey col-xs-12',
25712 //width: this.meterWidth + 'px'
25716 cls: 'roo-password-meter-text'
25722 if (this.hideTrigger) {
25723 this.trigger.setDisplayed(false);
25725 this.setSize(this.width || '', this.height || '');
25728 onDestroy: function ()
25730 if (this.trigger) {
25731 this.trigger.removeAllListeners();
25732 this.trigger.remove();
25735 this.wrap.remove();
25737 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25740 checkStrength: function ()
25742 var pwd = this.inputEl().getValue();
25743 if (pwd == this._lastPwd) {
25748 if (this.ClientSideStrongPassword(pwd)) {
25750 } else if (this.ClientSideMediumPassword(pwd)) {
25752 } else if (this.ClientSideWeakPassword(pwd)) {
25758 Roo.log('strength1: ' + strength);
25760 //var pm = this.trigger.child('div/div/div').dom;
25761 var pm = this.trigger.child('div/div');
25762 pm.removeClass(this.meterClass);
25763 pm.addClass(this.meterClass[strength]);
25766 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25768 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25770 this._lastPwd = pwd;
25774 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25776 this._lastPwd = '';
25778 var pm = this.trigger.child('div/div');
25779 pm.removeClass(this.meterClass);
25780 pm.addClass('roo-password-meter-grey');
25783 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25786 this.inputEl().dom.type='password';
25789 validateValue: function (value)
25791 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25794 if (value.length == 0) {
25795 if (this.allowBlank) {
25796 this.clearInvalid();
25800 this.markInvalid(this.errors.PwdEmpty);
25801 this.errorMsg = this.errors.PwdEmpty;
25809 if (!value.match(/[\x21-\x7e]+/)) {
25810 this.markInvalid(this.errors.PwdBadChar);
25811 this.errorMsg = this.errors.PwdBadChar;
25814 if (value.length < 6) {
25815 this.markInvalid(this.errors.PwdShort);
25816 this.errorMsg = this.errors.PwdShort;
25819 if (value.length > 16) {
25820 this.markInvalid(this.errors.PwdLong);
25821 this.errorMsg = this.errors.PwdLong;
25825 if (this.ClientSideStrongPassword(value)) {
25827 } else if (this.ClientSideMediumPassword(value)) {
25829 } else if (this.ClientSideWeakPassword(value)) {
25836 if (strength < 2) {
25837 //this.markInvalid(this.errors.TooWeak);
25838 this.errorMsg = this.errors.TooWeak;
25843 console.log('strength2: ' + strength);
25845 //var pm = this.trigger.child('div/div/div').dom;
25847 var pm = this.trigger.child('div/div');
25848 pm.removeClass(this.meterClass);
25849 pm.addClass(this.meterClass[strength]);
25851 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25853 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25855 this.errorMsg = '';
25859 CharacterSetChecks: function (type)
25862 this.fResult = false;
25865 isctype: function (character, type)
25868 case this.kCapitalLetter:
25869 if (character >= 'A' && character <= 'Z') {
25874 case this.kSmallLetter:
25875 if (character >= 'a' && character <= 'z') {
25881 if (character >= '0' && character <= '9') {
25886 case this.kPunctuation:
25887 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25898 IsLongEnough: function (pwd, size)
25900 return !(pwd == null || isNaN(size) || pwd.length < size);
25903 SpansEnoughCharacterSets: function (word, nb)
25905 if (!this.IsLongEnough(word, nb))
25910 var characterSetChecks = new Array(
25911 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25912 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25915 for (var index = 0; index < word.length; ++index) {
25916 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25917 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25918 characterSetChecks[nCharSet].fResult = true;
25925 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25926 if (characterSetChecks[nCharSet].fResult) {
25931 if (nCharSets < nb) {
25937 ClientSideStrongPassword: function (pwd)
25939 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25942 ClientSideMediumPassword: function (pwd)
25944 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25947 ClientSideWeakPassword: function (pwd)
25949 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25952 })//<script type="text/javascript">
25955 * Based Ext JS Library 1.1.1
25956 * Copyright(c) 2006-2007, Ext JS, LLC.
25962 * @class Roo.HtmlEditorCore
25963 * @extends Roo.Component
25964 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25966 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25969 Roo.HtmlEditorCore = function(config){
25972 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25977 * @event initialize
25978 * Fires when the editor is fully initialized (including the iframe)
25979 * @param {Roo.HtmlEditorCore} this
25984 * Fires when the editor is first receives the focus. Any insertion must wait
25985 * until after this event.
25986 * @param {Roo.HtmlEditorCore} this
25990 * @event beforesync
25991 * Fires before the textarea is updated with content from the editor iframe. Return false
25992 * to cancel the sync.
25993 * @param {Roo.HtmlEditorCore} this
25994 * @param {String} html
25998 * @event beforepush
25999 * Fires before the iframe editor is updated with content from the textarea. Return false
26000 * to cancel the push.
26001 * @param {Roo.HtmlEditorCore} this
26002 * @param {String} html
26007 * Fires when the textarea is updated with content from the editor iframe.
26008 * @param {Roo.HtmlEditorCore} this
26009 * @param {String} html
26014 * Fires when the iframe editor is updated with content from the textarea.
26015 * @param {Roo.HtmlEditorCore} this
26016 * @param {String} html
26021 * @event editorevent
26022 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26023 * @param {Roo.HtmlEditorCore} this
26029 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26031 // defaults : white / black...
26032 this.applyBlacklists();
26039 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
26043 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
26049 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26054 * @cfg {Number} height (in pixels)
26058 * @cfg {Number} width (in pixels)
26063 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26066 stylesheets: false,
26069 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26071 allowComments: false,
26075 // private properties
26076 validationEvent : false,
26078 initialized : false,
26080 sourceEditMode : false,
26081 onFocus : Roo.emptyFn,
26083 hideMode:'offsets',
26087 // blacklist + whitelisted elements..
26094 * Protected method that will not generally be called directly. It
26095 * is called when the editor initializes the iframe with HTML contents. Override this method if you
26096 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26098 getDocMarkup : function(){
26102 // inherit styels from page...??
26103 if (this.stylesheets === false) {
26105 Roo.get(document.head).select('style').each(function(node) {
26106 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26109 Roo.get(document.head).select('link').each(function(node) {
26110 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26113 } else if (!this.stylesheets.length) {
26115 st = '<style type="text/css">' +
26116 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26119 for (var i in this.stylesheets) {
26120 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26125 st += '<style type="text/css">' +
26126 'IMG { cursor: pointer } ' +
26129 var cls = 'roo-htmleditor-body';
26131 if(this.bodyCls.length){
26132 cls += ' ' + this.bodyCls;
26135 return '<html><head>' + st +
26136 //<style type="text/css">' +
26137 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26139 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
26143 onRender : function(ct, position)
26146 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26147 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26150 this.el.dom.style.border = '0 none';
26151 this.el.dom.setAttribute('tabIndex', -1);
26152 this.el.addClass('x-hidden hide');
26156 if(Roo.isIE){ // fix IE 1px bogus margin
26157 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26161 this.frameId = Roo.id();
26165 var iframe = this.owner.wrap.createChild({
26167 cls: 'form-control', // bootstrap..
26169 name: this.frameId,
26170 frameBorder : 'no',
26171 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
26176 this.iframe = iframe.dom;
26178 this.assignDocWin();
26180 this.doc.designMode = 'on';
26183 this.doc.write(this.getDocMarkup());
26187 var task = { // must defer to wait for browser to be ready
26189 //console.log("run task?" + this.doc.readyState);
26190 this.assignDocWin();
26191 if(this.doc.body || this.doc.readyState == 'complete'){
26193 this.doc.designMode="on";
26197 Roo.TaskMgr.stop(task);
26198 this.initEditor.defer(10, this);
26205 Roo.TaskMgr.start(task);
26210 onResize : function(w, h)
26212 Roo.log('resize: ' +w + ',' + h );
26213 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26217 if(typeof w == 'number'){
26219 this.iframe.style.width = w + 'px';
26221 if(typeof h == 'number'){
26223 this.iframe.style.height = h + 'px';
26225 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26232 * Toggles the editor between standard and source edit mode.
26233 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26235 toggleSourceEdit : function(sourceEditMode){
26237 this.sourceEditMode = sourceEditMode === true;
26239 if(this.sourceEditMode){
26241 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
26244 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26245 //this.iframe.className = '';
26248 //this.setSize(this.owner.wrap.getSize());
26249 //this.fireEvent('editmodechange', this, this.sourceEditMode);
26256 * Protected method that will not generally be called directly. If you need/want
26257 * custom HTML cleanup, this is the method you should override.
26258 * @param {String} html The HTML to be cleaned
26259 * return {String} The cleaned HTML
26261 cleanHtml : function(html){
26262 html = String(html);
26263 if(html.length > 5){
26264 if(Roo.isSafari){ // strip safari nonsense
26265 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26268 if(html == ' '){
26275 * HTML Editor -> Textarea
26276 * Protected method that will not generally be called directly. Syncs the contents
26277 * of the editor iframe with the textarea.
26279 syncValue : function(){
26280 if(this.initialized){
26281 var bd = (this.doc.body || this.doc.documentElement);
26282 //this.cleanUpPaste(); -- this is done else where and causes havoc..
26283 var html = bd.innerHTML;
26285 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26286 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26288 html = '<div style="'+m[0]+'">' + html + '</div>';
26291 html = this.cleanHtml(html);
26292 // fix up the special chars.. normaly like back quotes in word...
26293 // however we do not want to do this with chinese..
26294 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26296 var cc = match.charCodeAt();
26298 // Get the character value, handling surrogate pairs
26299 if (match.length == 2) {
26300 // It's a surrogate pair, calculate the Unicode code point
26301 var high = match.charCodeAt(0) - 0xD800;
26302 var low = match.charCodeAt(1) - 0xDC00;
26303 cc = (high * 0x400) + low + 0x10000;
26305 (cc >= 0x4E00 && cc < 0xA000 ) ||
26306 (cc >= 0x3400 && cc < 0x4E00 ) ||
26307 (cc >= 0xf900 && cc < 0xfb00 )
26312 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26313 return "&#" + cc + ";";
26320 if(this.owner.fireEvent('beforesync', this, html) !== false){
26321 this.el.dom.value = html;
26322 this.owner.fireEvent('sync', this, html);
26328 * Protected method that will not generally be called directly. Pushes the value of the textarea
26329 * into the iframe editor.
26331 pushValue : function(){
26332 if(this.initialized){
26333 var v = this.el.dom.value.trim();
26335 // if(v.length < 1){
26339 if(this.owner.fireEvent('beforepush', this, v) !== false){
26340 var d = (this.doc.body || this.doc.documentElement);
26342 this.cleanUpPaste();
26343 this.el.dom.value = d.innerHTML;
26344 this.owner.fireEvent('push', this, v);
26350 deferFocus : function(){
26351 this.focus.defer(10, this);
26355 focus : function(){
26356 if(this.win && !this.sourceEditMode){
26363 assignDocWin: function()
26365 var iframe = this.iframe;
26368 this.doc = iframe.contentWindow.document;
26369 this.win = iframe.contentWindow;
26371 // if (!Roo.get(this.frameId)) {
26374 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26375 // this.win = Roo.get(this.frameId).dom.contentWindow;
26377 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26381 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26382 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26387 initEditor : function(){
26388 //console.log("INIT EDITOR");
26389 this.assignDocWin();
26393 this.doc.designMode="on";
26395 this.doc.write(this.getDocMarkup());
26398 var dbody = (this.doc.body || this.doc.documentElement);
26399 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26400 // this copies styles from the containing element into thsi one..
26401 // not sure why we need all of this..
26402 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26404 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26405 //ss['background-attachment'] = 'fixed'; // w3c
26406 dbody.bgProperties = 'fixed'; // ie
26407 //Roo.DomHelper.applyStyles(dbody, ss);
26408 Roo.EventManager.on(this.doc, {
26409 //'mousedown': this.onEditorEvent,
26410 'mouseup': this.onEditorEvent,
26411 'dblclick': this.onEditorEvent,
26412 'click': this.onEditorEvent,
26413 'keyup': this.onEditorEvent,
26418 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26420 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26421 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26423 this.initialized = true;
26425 this.owner.fireEvent('initialize', this);
26430 onDestroy : function(){
26436 //for (var i =0; i < this.toolbars.length;i++) {
26437 // // fixme - ask toolbars for heights?
26438 // this.toolbars[i].onDestroy();
26441 //this.wrap.dom.innerHTML = '';
26442 //this.wrap.remove();
26447 onFirstFocus : function(){
26449 this.assignDocWin();
26452 this.activated = true;
26455 if(Roo.isGecko){ // prevent silly gecko errors
26457 var s = this.win.getSelection();
26458 if(!s.focusNode || s.focusNode.nodeType != 3){
26459 var r = s.getRangeAt(0);
26460 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26465 this.execCmd('useCSS', true);
26466 this.execCmd('styleWithCSS', false);
26469 this.owner.fireEvent('activate', this);
26473 adjustFont: function(btn){
26474 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26475 //if(Roo.isSafari){ // safari
26478 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26479 if(Roo.isSafari){ // safari
26480 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26481 v = (v < 10) ? 10 : v;
26482 v = (v > 48) ? 48 : v;
26483 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26488 v = Math.max(1, v+adjust);
26490 this.execCmd('FontSize', v );
26493 onEditorEvent : function(e)
26495 this.owner.fireEvent('editorevent', this, e);
26496 // this.updateToolbar();
26497 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26500 insertTag : function(tg)
26502 // could be a bit smarter... -> wrap the current selected tRoo..
26503 if (tg.toLowerCase() == 'span' ||
26504 tg.toLowerCase() == 'code' ||
26505 tg.toLowerCase() == 'sup' ||
26506 tg.toLowerCase() == 'sub'
26509 range = this.createRange(this.getSelection());
26510 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26511 wrappingNode.appendChild(range.extractContents());
26512 range.insertNode(wrappingNode);
26519 this.execCmd("formatblock", tg);
26523 insertText : function(txt)
26527 var range = this.createRange();
26528 range.deleteContents();
26529 //alert(Sender.getAttribute('label'));
26531 range.insertNode(this.doc.createTextNode(txt));
26537 * Executes a Midas editor command on the editor document and performs necessary focus and
26538 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26539 * @param {String} cmd The Midas command
26540 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26542 relayCmd : function(cmd, value){
26544 this.execCmd(cmd, value);
26545 this.owner.fireEvent('editorevent', this);
26546 //this.updateToolbar();
26547 this.owner.deferFocus();
26551 * Executes a Midas editor command directly on the editor document.
26552 * For visual commands, you should use {@link #relayCmd} instead.
26553 * <b>This should only be called after the editor is initialized.</b>
26554 * @param {String} cmd The Midas command
26555 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26557 execCmd : function(cmd, value){
26558 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26565 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26567 * @param {String} text | dom node..
26569 insertAtCursor : function(text)
26572 if(!this.activated){
26578 var r = this.doc.selection.createRange();
26589 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26593 // from jquery ui (MIT licenced)
26595 var win = this.win;
26597 if (win.getSelection && win.getSelection().getRangeAt) {
26598 range = win.getSelection().getRangeAt(0);
26599 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26600 range.insertNode(node);
26601 } else if (win.document.selection && win.document.selection.createRange) {
26602 // no firefox support
26603 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26604 win.document.selection.createRange().pasteHTML(txt);
26606 // no firefox support
26607 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26608 this.execCmd('InsertHTML', txt);
26617 mozKeyPress : function(e){
26619 var c = e.getCharCode(), cmd;
26622 c = String.fromCharCode(c).toLowerCase();
26636 this.cleanUpPaste.defer(100, this);
26644 e.preventDefault();
26652 fixKeys : function(){ // load time branching for fastest keydown performance
26654 return function(e){
26655 var k = e.getKey(), r;
26658 r = this.doc.selection.createRange();
26661 r.pasteHTML('    ');
26668 r = this.doc.selection.createRange();
26670 var target = r.parentElement();
26671 if(!target || target.tagName.toLowerCase() != 'li'){
26673 r.pasteHTML('<br />');
26679 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26680 this.cleanUpPaste.defer(100, this);
26686 }else if(Roo.isOpera){
26687 return function(e){
26688 var k = e.getKey();
26692 this.execCmd('InsertHTML','    ');
26695 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26696 this.cleanUpPaste.defer(100, this);
26701 }else if(Roo.isSafari){
26702 return function(e){
26703 var k = e.getKey();
26707 this.execCmd('InsertText','\t');
26711 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26712 this.cleanUpPaste.defer(100, this);
26720 getAllAncestors: function()
26722 var p = this.getSelectedNode();
26725 a.push(p); // push blank onto stack..
26726 p = this.getParentElement();
26730 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26734 a.push(this.doc.body);
26738 lastSelNode : false,
26741 getSelection : function()
26743 this.assignDocWin();
26744 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26747 getSelectedNode: function()
26749 // this may only work on Gecko!!!
26751 // should we cache this!!!!
26756 var range = this.createRange(this.getSelection()).cloneRange();
26759 var parent = range.parentElement();
26761 var testRange = range.duplicate();
26762 testRange.moveToElementText(parent);
26763 if (testRange.inRange(range)) {
26766 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26769 parent = parent.parentElement;
26774 // is ancestor a text element.
26775 var ac = range.commonAncestorContainer;
26776 if (ac.nodeType == 3) {
26777 ac = ac.parentNode;
26780 var ar = ac.childNodes;
26783 var other_nodes = [];
26784 var has_other_nodes = false;
26785 for (var i=0;i<ar.length;i++) {
26786 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26789 // fullly contained node.
26791 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26796 // probably selected..
26797 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26798 other_nodes.push(ar[i]);
26802 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26807 has_other_nodes = true;
26809 if (!nodes.length && other_nodes.length) {
26810 nodes= other_nodes;
26812 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26818 createRange: function(sel)
26820 // this has strange effects when using with
26821 // top toolbar - not sure if it's a great idea.
26822 //this.editor.contentWindow.focus();
26823 if (typeof sel != "undefined") {
26825 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26827 return this.doc.createRange();
26830 return this.doc.createRange();
26833 getParentElement: function()
26836 this.assignDocWin();
26837 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26839 var range = this.createRange(sel);
26842 var p = range.commonAncestorContainer;
26843 while (p.nodeType == 3) { // text node
26854 * Range intersection.. the hard stuff...
26858 * [ -- selected range --- ]
26862 * if end is before start or hits it. fail.
26863 * if start is after end or hits it fail.
26865 * if either hits (but other is outside. - then it's not
26871 // @see http://www.thismuchiknow.co.uk/?p=64.
26872 rangeIntersectsNode : function(range, node)
26874 var nodeRange = node.ownerDocument.createRange();
26876 nodeRange.selectNode(node);
26878 nodeRange.selectNodeContents(node);
26881 var rangeStartRange = range.cloneRange();
26882 rangeStartRange.collapse(true);
26884 var rangeEndRange = range.cloneRange();
26885 rangeEndRange.collapse(false);
26887 var nodeStartRange = nodeRange.cloneRange();
26888 nodeStartRange.collapse(true);
26890 var nodeEndRange = nodeRange.cloneRange();
26891 nodeEndRange.collapse(false);
26893 return rangeStartRange.compareBoundaryPoints(
26894 Range.START_TO_START, nodeEndRange) == -1 &&
26895 rangeEndRange.compareBoundaryPoints(
26896 Range.START_TO_START, nodeStartRange) == 1;
26900 rangeCompareNode : function(range, node)
26902 var nodeRange = node.ownerDocument.createRange();
26904 nodeRange.selectNode(node);
26906 nodeRange.selectNodeContents(node);
26910 range.collapse(true);
26912 nodeRange.collapse(true);
26914 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26915 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26917 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26919 var nodeIsBefore = ss == 1;
26920 var nodeIsAfter = ee == -1;
26922 if (nodeIsBefore && nodeIsAfter) {
26925 if (!nodeIsBefore && nodeIsAfter) {
26926 return 1; //right trailed.
26929 if (nodeIsBefore && !nodeIsAfter) {
26930 return 2; // left trailed.
26936 // private? - in a new class?
26937 cleanUpPaste : function()
26939 // cleans up the whole document..
26940 Roo.log('cleanuppaste');
26942 this.cleanUpChildren(this.doc.body);
26943 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26944 if (clean != this.doc.body.innerHTML) {
26945 this.doc.body.innerHTML = clean;
26950 cleanWordChars : function(input) {// change the chars to hex code
26951 var he = Roo.HtmlEditorCore;
26953 var output = input;
26954 Roo.each(he.swapCodes, function(sw) {
26955 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26957 output = output.replace(swapper, sw[1]);
26964 cleanUpChildren : function (n)
26966 if (!n.childNodes.length) {
26969 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26970 this.cleanUpChild(n.childNodes[i]);
26977 cleanUpChild : function (node)
26980 //console.log(node);
26981 if (node.nodeName == "#text") {
26982 // clean up silly Windows -- stuff?
26985 if (node.nodeName == "#comment") {
26986 if (!this.allowComments) {
26987 node.parentNode.removeChild(node);
26989 // clean up silly Windows -- stuff?
26992 var lcname = node.tagName.toLowerCase();
26993 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26994 // whitelist of tags..
26996 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26998 node.parentNode.removeChild(node);
27003 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27005 // spans with no attributes - just remove them..
27006 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
27007 remove_keep_children = true;
27010 // remove <a name=....> as rendering on yahoo mailer is borked with this.
27011 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27013 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27014 // remove_keep_children = true;
27017 if (remove_keep_children) {
27018 this.cleanUpChildren(node);
27019 // inserts everything just before this node...
27020 while (node.childNodes.length) {
27021 var cn = node.childNodes[0];
27022 node.removeChild(cn);
27023 node.parentNode.insertBefore(cn, node);
27025 node.parentNode.removeChild(node);
27029 if (!node.attributes || !node.attributes.length) {
27034 this.cleanUpChildren(node);
27038 function cleanAttr(n,v)
27041 if (v.match(/^\./) || v.match(/^\//)) {
27044 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27047 if (v.match(/^#/)) {
27050 if (v.match(/^\{/)) { // allow template editing.
27053 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27054 node.removeAttribute(n);
27058 var cwhite = this.cwhite;
27059 var cblack = this.cblack;
27061 function cleanStyle(n,v)
27063 if (v.match(/expression/)) { //XSS?? should we even bother..
27064 node.removeAttribute(n);
27068 var parts = v.split(/;/);
27071 Roo.each(parts, function(p) {
27072 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27076 var l = p.split(':').shift().replace(/\s+/g,'');
27077 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27079 if ( cwhite.length && cblack.indexOf(l) > -1) {
27080 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27081 //node.removeAttribute(n);
27085 // only allow 'c whitelisted system attributes'
27086 if ( cwhite.length && cwhite.indexOf(l) < 0) {
27087 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27088 //node.removeAttribute(n);
27098 if (clean.length) {
27099 node.setAttribute(n, clean.join(';'));
27101 node.removeAttribute(n);
27107 for (var i = node.attributes.length-1; i > -1 ; i--) {
27108 var a = node.attributes[i];
27111 if (a.name.toLowerCase().substr(0,2)=='on') {
27112 node.removeAttribute(a.name);
27115 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27116 node.removeAttribute(a.name);
27119 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27120 cleanAttr(a.name,a.value); // fixme..
27123 if (a.name == 'style') {
27124 cleanStyle(a.name,a.value);
27127 /// clean up MS crap..
27128 // tecnically this should be a list of valid class'es..
27131 if (a.name == 'class') {
27132 if (a.value.match(/^Mso/)) {
27133 node.removeAttribute('class');
27136 if (a.value.match(/^body$/)) {
27137 node.removeAttribute('class');
27148 this.cleanUpChildren(node);
27154 * Clean up MS wordisms...
27156 cleanWord : function(node)
27159 this.cleanWord(this.doc.body);
27164 node.nodeName == 'SPAN' &&
27165 !node.hasAttributes() &&
27166 node.childNodes.length == 1 &&
27167 node.firstChild.nodeName == "#text"
27169 var textNode = node.firstChild;
27170 node.removeChild(textNode);
27171 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27172 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27174 node.parentNode.insertBefore(textNode, node);
27175 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27176 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27178 node.parentNode.removeChild(node);
27181 if (node.nodeName == "#text") {
27182 // clean up silly Windows -- stuff?
27185 if (node.nodeName == "#comment") {
27186 node.parentNode.removeChild(node);
27187 // clean up silly Windows -- stuff?
27191 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27192 node.parentNode.removeChild(node);
27195 //Roo.log(node.tagName);
27196 // remove - but keep children..
27197 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27198 //Roo.log('-- removed');
27199 while (node.childNodes.length) {
27200 var cn = node.childNodes[0];
27201 node.removeChild(cn);
27202 node.parentNode.insertBefore(cn, node);
27203 // move node to parent - and clean it..
27204 this.cleanWord(cn);
27206 node.parentNode.removeChild(node);
27207 /// no need to iterate chidlren = it's got none..
27208 //this.iterateChildren(node, this.cleanWord);
27212 if (node.className.length) {
27214 var cn = node.className.split(/\W+/);
27216 Roo.each(cn, function(cls) {
27217 if (cls.match(/Mso[a-zA-Z]+/)) {
27222 node.className = cna.length ? cna.join(' ') : '';
27224 node.removeAttribute("class");
27228 if (node.hasAttribute("lang")) {
27229 node.removeAttribute("lang");
27232 if (node.hasAttribute("style")) {
27234 var styles = node.getAttribute("style").split(";");
27236 Roo.each(styles, function(s) {
27237 if (!s.match(/:/)) {
27240 var kv = s.split(":");
27241 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27244 // what ever is left... we allow.
27247 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27248 if (!nstyle.length) {
27249 node.removeAttribute('style');
27252 this.iterateChildren(node, this.cleanWord);
27258 * iterateChildren of a Node, calling fn each time, using this as the scole..
27259 * @param {DomNode} node node to iterate children of.
27260 * @param {Function} fn method of this class to call on each item.
27262 iterateChildren : function(node, fn)
27264 if (!node.childNodes.length) {
27267 for (var i = node.childNodes.length-1; i > -1 ; i--) {
27268 fn.call(this, node.childNodes[i])
27274 * cleanTableWidths.
27276 * Quite often pasting from word etc.. results in tables with column and widths.
27277 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27280 cleanTableWidths : function(node)
27285 this.cleanTableWidths(this.doc.body);
27290 if (node.nodeName == "#text" || node.nodeName == "#comment") {
27293 Roo.log(node.tagName);
27294 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27295 this.iterateChildren(node, this.cleanTableWidths);
27298 if (node.hasAttribute('width')) {
27299 node.removeAttribute('width');
27303 if (node.hasAttribute("style")) {
27306 var styles = node.getAttribute("style").split(";");
27308 Roo.each(styles, function(s) {
27309 if (!s.match(/:/)) {
27312 var kv = s.split(":");
27313 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27316 // what ever is left... we allow.
27319 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27320 if (!nstyle.length) {
27321 node.removeAttribute('style');
27325 this.iterateChildren(node, this.cleanTableWidths);
27333 domToHTML : function(currentElement, depth, nopadtext) {
27335 depth = depth || 0;
27336 nopadtext = nopadtext || false;
27338 if (!currentElement) {
27339 return this.domToHTML(this.doc.body);
27342 //Roo.log(currentElement);
27344 var allText = false;
27345 var nodeName = currentElement.nodeName;
27346 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27348 if (nodeName == '#text') {
27350 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27355 if (nodeName != 'BODY') {
27358 // Prints the node tagName, such as <A>, <IMG>, etc
27361 for(i = 0; i < currentElement.attributes.length;i++) {
27363 var aname = currentElement.attributes.item(i).name;
27364 if (!currentElement.attributes.item(i).value.length) {
27367 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27370 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27379 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27382 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27387 // Traverse the tree
27389 var currentElementChild = currentElement.childNodes.item(i);
27390 var allText = true;
27391 var innerHTML = '';
27393 while (currentElementChild) {
27394 // Formatting code (indent the tree so it looks nice on the screen)
27395 var nopad = nopadtext;
27396 if (lastnode == 'SPAN') {
27400 if (currentElementChild.nodeName == '#text') {
27401 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27402 toadd = nopadtext ? toadd : toadd.trim();
27403 if (!nopad && toadd.length > 80) {
27404 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27406 innerHTML += toadd;
27409 currentElementChild = currentElement.childNodes.item(i);
27415 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27417 // Recursively traverse the tree structure of the child node
27418 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27419 lastnode = currentElementChild.nodeName;
27421 currentElementChild=currentElement.childNodes.item(i);
27427 // The remaining code is mostly for formatting the tree
27428 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27433 ret+= "</"+tagName+">";
27439 applyBlacklists : function()
27441 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27442 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27446 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27447 if (b.indexOf(tag) > -1) {
27450 this.white.push(tag);
27454 Roo.each(w, function(tag) {
27455 if (b.indexOf(tag) > -1) {
27458 if (this.white.indexOf(tag) > -1) {
27461 this.white.push(tag);
27466 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27467 if (w.indexOf(tag) > -1) {
27470 this.black.push(tag);
27474 Roo.each(b, function(tag) {
27475 if (w.indexOf(tag) > -1) {
27478 if (this.black.indexOf(tag) > -1) {
27481 this.black.push(tag);
27486 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27487 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27491 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27492 if (b.indexOf(tag) > -1) {
27495 this.cwhite.push(tag);
27499 Roo.each(w, function(tag) {
27500 if (b.indexOf(tag) > -1) {
27503 if (this.cwhite.indexOf(tag) > -1) {
27506 this.cwhite.push(tag);
27511 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27512 if (w.indexOf(tag) > -1) {
27515 this.cblack.push(tag);
27519 Roo.each(b, function(tag) {
27520 if (w.indexOf(tag) > -1) {
27523 if (this.cblack.indexOf(tag) > -1) {
27526 this.cblack.push(tag);
27531 setStylesheets : function(stylesheets)
27533 if(typeof(stylesheets) == 'string'){
27534 Roo.get(this.iframe.contentDocument.head).createChild({
27536 rel : 'stylesheet',
27545 Roo.each(stylesheets, function(s) {
27550 Roo.get(_this.iframe.contentDocument.head).createChild({
27552 rel : 'stylesheet',
27561 removeStylesheets : function()
27565 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27570 setStyle : function(style)
27572 Roo.get(this.iframe.contentDocument.head).createChild({
27581 // hide stuff that is not compatible
27595 * @event specialkey
27599 * @cfg {String} fieldClass @hide
27602 * @cfg {String} focusClass @hide
27605 * @cfg {String} autoCreate @hide
27608 * @cfg {String} inputType @hide
27611 * @cfg {String} invalidClass @hide
27614 * @cfg {String} invalidText @hide
27617 * @cfg {String} msgFx @hide
27620 * @cfg {String} validateOnBlur @hide
27624 Roo.HtmlEditorCore.white = [
27625 'area', 'br', 'img', 'input', 'hr', 'wbr',
27627 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27628 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27629 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27630 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27631 'table', 'ul', 'xmp',
27633 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27636 'dir', 'menu', 'ol', 'ul', 'dl',
27642 Roo.HtmlEditorCore.black = [
27643 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27645 'base', 'basefont', 'bgsound', 'blink', 'body',
27646 'frame', 'frameset', 'head', 'html', 'ilayer',
27647 'iframe', 'layer', 'link', 'meta', 'object',
27648 'script', 'style' ,'title', 'xml' // clean later..
27650 Roo.HtmlEditorCore.clean = [
27651 'script', 'style', 'title', 'xml'
27653 Roo.HtmlEditorCore.remove = [
27658 Roo.HtmlEditorCore.ablack = [
27662 Roo.HtmlEditorCore.aclean = [
27663 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27667 Roo.HtmlEditorCore.pwhite= [
27668 'http', 'https', 'mailto'
27671 // white listed style attributes.
27672 Roo.HtmlEditorCore.cwhite= [
27673 // 'text-align', /// default is to allow most things..
27679 // black listed style attributes.
27680 Roo.HtmlEditorCore.cblack= [
27681 // 'font-size' -- this can be set by the project
27685 Roo.HtmlEditorCore.swapCodes =[
27686 [ 8211, "–" ],
27687 [ 8212, "—" ],
27704 * @class Roo.bootstrap.HtmlEditor
27705 * @extends Roo.bootstrap.TextArea
27706 * Bootstrap HtmlEditor class
27709 * Create a new HtmlEditor
27710 * @param {Object} config The config object
27713 Roo.bootstrap.HtmlEditor = function(config){
27714 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27715 if (!this.toolbars) {
27716 this.toolbars = [];
27719 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27722 * @event initialize
27723 * Fires when the editor is fully initialized (including the iframe)
27724 * @param {HtmlEditor} this
27729 * Fires when the editor is first receives the focus. Any insertion must wait
27730 * until after this event.
27731 * @param {HtmlEditor} this
27735 * @event beforesync
27736 * Fires before the textarea is updated with content from the editor iframe. Return false
27737 * to cancel the sync.
27738 * @param {HtmlEditor} this
27739 * @param {String} html
27743 * @event beforepush
27744 * Fires before the iframe editor is updated with content from the textarea. Return false
27745 * to cancel the push.
27746 * @param {HtmlEditor} this
27747 * @param {String} html
27752 * Fires when the textarea is updated with content from the editor iframe.
27753 * @param {HtmlEditor} this
27754 * @param {String} html
27759 * Fires when the iframe editor is updated with content from the textarea.
27760 * @param {HtmlEditor} this
27761 * @param {String} html
27765 * @event editmodechange
27766 * Fires when the editor switches edit modes
27767 * @param {HtmlEditor} this
27768 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27770 editmodechange: true,
27772 * @event editorevent
27773 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27774 * @param {HtmlEditor} this
27778 * @event firstfocus
27779 * Fires when on first focus - needed by toolbars..
27780 * @param {HtmlEditor} this
27785 * Auto save the htmlEditor value as a file into Events
27786 * @param {HtmlEditor} this
27790 * @event savedpreview
27791 * preview the saved version of htmlEditor
27792 * @param {HtmlEditor} this
27799 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27803 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27808 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27813 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27818 * @cfg {Number} height (in pixels)
27822 * @cfg {Number} width (in pixels)
27827 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27830 stylesheets: false,
27835 // private properties
27836 validationEvent : false,
27838 initialized : false,
27841 onFocus : Roo.emptyFn,
27843 hideMode:'offsets',
27845 tbContainer : false,
27849 toolbarContainer :function() {
27850 return this.wrap.select('.x-html-editor-tb',true).first();
27854 * Protected method that will not generally be called directly. It
27855 * is called when the editor creates its toolbar. Override this method if you need to
27856 * add custom toolbar buttons.
27857 * @param {HtmlEditor} editor
27859 createToolbar : function(){
27860 Roo.log('renewing');
27861 Roo.log("create toolbars");
27863 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27864 this.toolbars[0].render(this.toolbarContainer());
27868 // if (!editor.toolbars || !editor.toolbars.length) {
27869 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27872 // for (var i =0 ; i < editor.toolbars.length;i++) {
27873 // editor.toolbars[i] = Roo.factory(
27874 // typeof(editor.toolbars[i]) == 'string' ?
27875 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27876 // Roo.bootstrap.HtmlEditor);
27877 // editor.toolbars[i].init(editor);
27883 onRender : function(ct, position)
27885 // Roo.log("Call onRender: " + this.xtype);
27887 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27889 this.wrap = this.inputEl().wrap({
27890 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27893 this.editorcore.onRender(ct, position);
27895 if (this.resizable) {
27896 this.resizeEl = new Roo.Resizable(this.wrap, {
27900 minHeight : this.height,
27901 height: this.height,
27902 handles : this.resizable,
27905 resize : function(r, w, h) {
27906 _t.onResize(w,h); // -something
27912 this.createToolbar(this);
27915 if(!this.width && this.resizable){
27916 this.setSize(this.wrap.getSize());
27918 if (this.resizeEl) {
27919 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27920 // should trigger onReize..
27926 onResize : function(w, h)
27928 Roo.log('resize: ' +w + ',' + h );
27929 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27933 if(this.inputEl() ){
27934 if(typeof w == 'number'){
27935 var aw = w - this.wrap.getFrameWidth('lr');
27936 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27939 if(typeof h == 'number'){
27940 var tbh = -11; // fixme it needs to tool bar size!
27941 for (var i =0; i < this.toolbars.length;i++) {
27942 // fixme - ask toolbars for heights?
27943 tbh += this.toolbars[i].el.getHeight();
27944 //if (this.toolbars[i].footer) {
27945 // tbh += this.toolbars[i].footer.el.getHeight();
27953 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27954 ah -= 5; // knock a few pixes off for look..
27955 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27959 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27960 this.editorcore.onResize(ew,eh);
27965 * Toggles the editor between standard and source edit mode.
27966 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27968 toggleSourceEdit : function(sourceEditMode)
27970 this.editorcore.toggleSourceEdit(sourceEditMode);
27972 if(this.editorcore.sourceEditMode){
27973 Roo.log('editor - showing textarea');
27976 // Roo.log(this.syncValue());
27978 this.inputEl().removeClass(['hide', 'x-hidden']);
27979 this.inputEl().dom.removeAttribute('tabIndex');
27980 this.inputEl().focus();
27982 Roo.log('editor - hiding textarea');
27984 // Roo.log(this.pushValue());
27987 this.inputEl().addClass(['hide', 'x-hidden']);
27988 this.inputEl().dom.setAttribute('tabIndex', -1);
27989 //this.deferFocus();
27992 if(this.resizable){
27993 this.setSize(this.wrap.getSize());
27996 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27999 // private (for BoxComponent)
28000 adjustSize : Roo.BoxComponent.prototype.adjustSize,
28002 // private (for BoxComponent)
28003 getResizeEl : function(){
28007 // private (for BoxComponent)
28008 getPositionEl : function(){
28013 initEvents : function(){
28014 this.originalValue = this.getValue();
28018 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28021 // markInvalid : Roo.emptyFn,
28023 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28026 // clearInvalid : Roo.emptyFn,
28028 setValue : function(v){
28029 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
28030 this.editorcore.pushValue();
28035 deferFocus : function(){
28036 this.focus.defer(10, this);
28040 focus : function(){
28041 this.editorcore.focus();
28047 onDestroy : function(){
28053 for (var i =0; i < this.toolbars.length;i++) {
28054 // fixme - ask toolbars for heights?
28055 this.toolbars[i].onDestroy();
28058 this.wrap.dom.innerHTML = '';
28059 this.wrap.remove();
28064 onFirstFocus : function(){
28065 //Roo.log("onFirstFocus");
28066 this.editorcore.onFirstFocus();
28067 for (var i =0; i < this.toolbars.length;i++) {
28068 this.toolbars[i].onFirstFocus();
28074 syncValue : function()
28076 this.editorcore.syncValue();
28079 pushValue : function()
28081 this.editorcore.pushValue();
28085 // hide stuff that is not compatible
28099 * @event specialkey
28103 * @cfg {String} fieldClass @hide
28106 * @cfg {String} focusClass @hide
28109 * @cfg {String} autoCreate @hide
28112 * @cfg {String} inputType @hide
28116 * @cfg {String} invalidText @hide
28119 * @cfg {String} msgFx @hide
28122 * @cfg {String} validateOnBlur @hide
28131 Roo.namespace('Roo.bootstrap.htmleditor');
28133 * @class Roo.bootstrap.HtmlEditorToolbar1
28134 * @extends Roo.bootstrap.nav.Simplebar
28140 new Roo.bootstrap.HtmlEditor({
28143 new Roo.bootstrap.HtmlEditorToolbar1({
28144 disable : { fonts: 1 , format: 1, ..., ... , ...],
28150 * @cfg {Object} disable List of elements to disable..
28151 * @cfg {Array} btns List of additional buttons.
28155 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28158 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
28161 Roo.apply(this, config);
28163 // default disabled, based on 'good practice'..
28164 this.disable = this.disable || {};
28165 Roo.applyIf(this.disable, {
28168 specialElements : true
28170 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
28172 this.editor = config.editor;
28173 this.editorcore = config.editor.editorcore;
28175 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28177 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28178 // dont call parent... till later.
28180 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.nav.Simplebar, {
28185 editorcore : false,
28190 "h1","h2","h3","h4","h5","h6",
28192 "abbr", "acronym", "address", "cite", "samp", "var",
28196 onRender : function(ct, position)
28198 // Roo.log("Call onRender: " + this.xtype);
28200 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
28202 this.el.dom.style.marginBottom = '0';
28204 var editorcore = this.editorcore;
28205 var editor= this.editor;
28208 var btn = function(id,cmd , toggle, handler, html){
28210 var event = toggle ? 'toggle' : 'click';
28215 xns: Roo.bootstrap,
28219 enableToggle:toggle !== false,
28221 pressed : toggle ? false : null,
28224 a.listeners[toggle ? 'toggle' : 'click'] = function() {
28225 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
28231 // var cb_box = function...
28236 xns: Roo.bootstrap,
28241 xns: Roo.bootstrap,
28245 Roo.each(this.formats, function(f) {
28246 style.menu.items.push({
28248 xns: Roo.bootstrap,
28249 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28254 editorcore.insertTag(this.tagname);
28261 children.push(style);
28263 btn('bold',false,true);
28264 btn('italic',false,true);
28265 btn('align-left', 'justifyleft',true);
28266 btn('align-center', 'justifycenter',true);
28267 btn('align-right' , 'justifyright',true);
28268 btn('link', false, false, function(btn) {
28269 //Roo.log("create link?");
28270 var url = prompt(this.createLinkText, this.defaultLinkValue);
28271 if(url && url != 'http:/'+'/'){
28272 this.editorcore.relayCmd('createlink', url);
28275 btn('list','insertunorderedlist',true);
28276 btn('pencil', false,true, function(btn){
28278 this.toggleSourceEdit(btn.pressed);
28281 if (this.editor.btns.length > 0) {
28282 for (var i = 0; i<this.editor.btns.length; i++) {
28283 children.push(this.editor.btns[i]);
28291 xns: Roo.bootstrap,
28296 xns: Roo.bootstrap,
28301 cog.menu.items.push({
28303 xns: Roo.bootstrap,
28304 html : Clean styles,
28309 editorcore.insertTag(this.tagname);
28318 this.xtype = 'NavSimplebar';
28320 for(var i=0;i< children.length;i++) {
28322 this.buttons.add(this.addxtypeChild(children[i]));
28326 editor.on('editorevent', this.updateToolbar, this);
28328 onBtnClick : function(id)
28330 this.editorcore.relayCmd(id);
28331 this.editorcore.focus();
28335 * Protected method that will not generally be called directly. It triggers
28336 * a toolbar update by reading the markup state of the current selection in the editor.
28338 updateToolbar: function(){
28340 if(!this.editorcore.activated){
28341 this.editor.onFirstFocus(); // is this neeed?
28345 var btns = this.buttons;
28346 var doc = this.editorcore.doc;
28347 btns.get('bold').setActive(doc.queryCommandState('bold'));
28348 btns.get('italic').setActive(doc.queryCommandState('italic'));
28349 //btns.get('underline').setActive(doc.queryCommandState('underline'));
28351 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28352 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28353 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28355 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28356 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28359 var ans = this.editorcore.getAllAncestors();
28360 if (this.formatCombo) {
28363 var store = this.formatCombo.store;
28364 this.formatCombo.setValue("");
28365 for (var i =0; i < ans.length;i++) {
28366 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28368 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28376 // hides menus... - so this cant be on a menu...
28377 Roo.bootstrap.MenuMgr.hideAll();
28379 Roo.bootstrap.menu.Manager.hideAll();
28380 //this.editorsyncValue();
28382 onFirstFocus: function() {
28383 this.buttons.each(function(item){
28387 toggleSourceEdit : function(sourceEditMode){
28390 if(sourceEditMode){
28391 Roo.log("disabling buttons");
28392 this.buttons.each( function(item){
28393 if(item.cmd != 'pencil'){
28399 Roo.log("enabling buttons");
28400 if(this.editorcore.initialized){
28401 this.buttons.each( function(item){
28407 Roo.log("calling toggole on editor");
28408 // tell the editor that it's been pressed..
28409 this.editor.toggleSourceEdit(sourceEditMode);
28423 * @class Roo.bootstrap.Markdown
28424 * @extends Roo.bootstrap.TextArea
28425 * Bootstrap Showdown editable area
28426 * @cfg {string} content
28429 * Create a new Showdown
28432 Roo.bootstrap.Markdown = function(config){
28433 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28437 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
28441 initEvents : function()
28444 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28445 this.markdownEl = this.el.createChild({
28446 cls : 'roo-markdown-area'
28448 this.inputEl().addClass('d-none');
28449 if (this.getValue() == '') {
28450 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28453 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28455 this.markdownEl.on('click', this.toggleTextEdit, this);
28456 this.on('blur', this.toggleTextEdit, this);
28457 this.on('specialkey', this.resizeTextArea, this);
28460 toggleTextEdit : function()
28462 var sh = this.markdownEl.getHeight();
28463 this.inputEl().addClass('d-none');
28464 this.markdownEl.addClass('d-none');
28465 if (!this.editing) {
28467 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28468 this.inputEl().removeClass('d-none');
28469 this.inputEl().focus();
28470 this.editing = true;
28473 // show showdown...
28474 this.updateMarkdown();
28475 this.markdownEl.removeClass('d-none');
28476 this.editing = false;
28479 updateMarkdown : function()
28481 if (this.getValue() == '') {
28482 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28486 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28489 resizeTextArea: function () {
28492 Roo.log([sh, this.getValue().split("\n").length * 30]);
28493 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28495 setValue : function(val)
28497 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28498 if (!this.editing) {
28499 this.updateMarkdown();
28505 if (!this.editing) {
28506 this.toggleTextEdit();
28514 * Ext JS Library 1.1.1
28515 * Copyright(c) 2006-2007, Ext JS, LLC.
28517 * Originally Released Under LGPL - original licence link has changed is not relivant.
28520 * <script type="text/javascript">
28524 * @class Roo.bootstrap.PagingToolbar
28525 * @extends Roo.bootstrap.nav.Simplebar
28526 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28528 * Create a new PagingToolbar
28529 * @param {Object} config The config object
28530 * @param {Roo.data.Store} store
28532 Roo.bootstrap.PagingToolbar = function(config)
28534 // old args format still supported... - xtype is prefered..
28535 // created from xtype...
28537 this.ds = config.dataSource;
28539 if (config.store && !this.ds) {
28540 this.store= Roo.factory(config.store, Roo.data);
28541 this.ds = this.store;
28542 this.ds.xmodule = this.xmodule || false;
28545 this.toolbarItems = [];
28546 if (config.items) {
28547 this.toolbarItems = config.items;
28550 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28555 this.bind(this.ds);
28558 if (Roo.bootstrap.version == 4) {
28559 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28561 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28566 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28568 * @cfg {Roo.bootstrap.Button} buttons[]
28569 * Buttons for the toolbar
28572 * @cfg {Roo.data.Store} store
28573 * The underlying data store providing the paged data
28576 * @cfg {String/HTMLElement/Element} container
28577 * container The id or element that will contain the toolbar
28580 * @cfg {Boolean} displayInfo
28581 * True to display the displayMsg (defaults to false)
28584 * @cfg {Number} pageSize
28585 * The number of records to display per page (defaults to 20)
28589 * @cfg {String} displayMsg
28590 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28592 displayMsg : 'Displaying {0} - {1} of {2}',
28594 * @cfg {String} emptyMsg
28595 * The message to display when no records are found (defaults to "No data to display")
28597 emptyMsg : 'No data to display',
28599 * Customizable piece of the default paging text (defaults to "Page")
28602 beforePageText : "Page",
28604 * Customizable piece of the default paging text (defaults to "of %0")
28607 afterPageText : "of {0}",
28609 * Customizable piece of the default paging text (defaults to "First Page")
28612 firstText : "First Page",
28614 * Customizable piece of the default paging text (defaults to "Previous Page")
28617 prevText : "Previous Page",
28619 * Customizable piece of the default paging text (defaults to "Next Page")
28622 nextText : "Next Page",
28624 * Customizable piece of the default paging text (defaults to "Last Page")
28627 lastText : "Last Page",
28629 * Customizable piece of the default paging text (defaults to "Refresh")
28632 refreshText : "Refresh",
28636 onRender : function(ct, position)
28638 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28639 this.navgroup.parentId = this.id;
28640 this.navgroup.onRender(this.el, null);
28641 // add the buttons to the navgroup
28643 if(this.displayInfo){
28644 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28645 this.displayEl = this.el.select('.x-paging-info', true).first();
28646 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28647 // this.displayEl = navel.el.select('span',true).first();
28653 Roo.each(_this.buttons, function(e){ // this might need to use render????
28654 Roo.factory(e).render(_this.el);
28658 Roo.each(_this.toolbarItems, function(e) {
28659 _this.navgroup.addItem(e);
28663 this.first = this.navgroup.addItem({
28664 tooltip: this.firstText,
28665 cls: "prev btn-outline-secondary",
28666 html : ' <i class="fa fa-step-backward"></i>',
28668 preventDefault: true,
28669 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28672 this.prev = this.navgroup.addItem({
28673 tooltip: this.prevText,
28674 cls: "prev btn-outline-secondary",
28675 html : ' <i class="fa fa-backward"></i>',
28677 preventDefault: true,
28678 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28680 //this.addSeparator();
28683 var field = this.navgroup.addItem( {
28685 cls : 'x-paging-position btn-outline-secondary',
28687 html : this.beforePageText +
28688 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28689 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28692 this.field = field.el.select('input', true).first();
28693 this.field.on("keydown", this.onPagingKeydown, this);
28694 this.field.on("focus", function(){this.dom.select();});
28697 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28698 //this.field.setHeight(18);
28699 //this.addSeparator();
28700 this.next = this.navgroup.addItem({
28701 tooltip: this.nextText,
28702 cls: "next btn-outline-secondary",
28703 html : ' <i class="fa fa-forward"></i>',
28705 preventDefault: true,
28706 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28708 this.last = this.navgroup.addItem({
28709 tooltip: this.lastText,
28710 html : ' <i class="fa fa-step-forward"></i>',
28711 cls: "next btn-outline-secondary",
28713 preventDefault: true,
28714 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28716 //this.addSeparator();
28717 this.loading = this.navgroup.addItem({
28718 tooltip: this.refreshText,
28719 cls: "btn-outline-secondary",
28720 html : ' <i class="fa fa-refresh"></i>',
28721 preventDefault: true,
28722 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28728 updateInfo : function(){
28729 if(this.displayEl){
28730 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28731 var msg = count == 0 ?
28735 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28737 this.displayEl.update(msg);
28742 onLoad : function(ds, r, o)
28744 this.cursor = o.params && o.params.start ? o.params.start : 0;
28746 var d = this.getPageData(),
28751 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28752 this.field.dom.value = ap;
28753 this.first.setDisabled(ap == 1);
28754 this.prev.setDisabled(ap == 1);
28755 this.next.setDisabled(ap == ps);
28756 this.last.setDisabled(ap == ps);
28757 this.loading.enable();
28762 getPageData : function(){
28763 var total = this.ds.getTotalCount();
28766 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28767 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28772 onLoadError : function(){
28773 this.loading.enable();
28777 onPagingKeydown : function(e){
28778 var k = e.getKey();
28779 var d = this.getPageData();
28781 var v = this.field.dom.value, pageNum;
28782 if(!v || isNaN(pageNum = parseInt(v, 10))){
28783 this.field.dom.value = d.activePage;
28786 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28787 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28790 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))
28792 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28793 this.field.dom.value = pageNum;
28794 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28797 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28799 var v = this.field.dom.value, pageNum;
28800 var increment = (e.shiftKey) ? 10 : 1;
28801 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28804 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28805 this.field.dom.value = d.activePage;
28808 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28810 this.field.dom.value = parseInt(v, 10) + increment;
28811 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28812 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28819 beforeLoad : function(){
28821 this.loading.disable();
28826 onClick : function(which){
28835 ds.load({params:{start: 0, limit: this.pageSize}});
28838 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28841 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28844 var total = ds.getTotalCount();
28845 var extra = total % this.pageSize;
28846 var lastStart = extra ? (total - extra) : total-this.pageSize;
28847 ds.load({params:{start: lastStart, limit: this.pageSize}});
28850 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28856 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28857 * @param {Roo.data.Store} store The data store to unbind
28859 unbind : function(ds){
28860 ds.un("beforeload", this.beforeLoad, this);
28861 ds.un("load", this.onLoad, this);
28862 ds.un("loadexception", this.onLoadError, this);
28863 ds.un("remove", this.updateInfo, this);
28864 ds.un("add", this.updateInfo, this);
28865 this.ds = undefined;
28869 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28870 * @param {Roo.data.Store} store The data store to bind
28872 bind : function(ds){
28873 ds.on("beforeload", this.beforeLoad, this);
28874 ds.on("load", this.onLoad, this);
28875 ds.on("loadexception", this.onLoadError, this);
28876 ds.on("remove", this.updateInfo, this);
28877 ds.on("add", this.updateInfo, this);
28888 * @class Roo.bootstrap.MessageBar
28889 * @extends Roo.bootstrap.Component
28890 * Bootstrap MessageBar class
28891 * @cfg {String} html contents of the MessageBar
28892 * @cfg {String} weight (info | success | warning | danger) default info
28893 * @cfg {String} beforeClass insert the bar before the given class
28894 * @cfg {Boolean} closable (true | false) default false
28895 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28898 * Create a new Element
28899 * @param {Object} config The config object
28902 Roo.bootstrap.MessageBar = function(config){
28903 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28906 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28912 beforeClass: 'bootstrap-sticky-wrap',
28914 getAutoCreate : function(){
28918 cls: 'alert alert-dismissable alert-' + this.weight,
28923 html: this.html || ''
28929 cfg.cls += ' alert-messages-fixed';
28943 onRender : function(ct, position)
28945 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28948 var cfg = Roo.apply({}, this.getAutoCreate());
28952 cfg.cls += ' ' + this.cls;
28955 cfg.style = this.style;
28957 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28959 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28962 this.el.select('>button.close').on('click', this.hide, this);
28968 if (!this.rendered) {
28974 this.fireEvent('show', this);
28980 if (!this.rendered) {
28986 this.fireEvent('hide', this);
28989 update : function()
28991 // var e = this.el.dom.firstChild;
28993 // if(this.closable){
28994 // e = e.nextSibling;
28997 // e.data = this.html || '';
28999 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29015 * @class Roo.bootstrap.Graph
29016 * @extends Roo.bootstrap.Component
29017 * Bootstrap Graph class
29021 @cfg {String} graphtype bar | vbar | pie
29022 @cfg {number} g_x coodinator | centre x (pie)
29023 @cfg {number} g_y coodinator | centre y (pie)
29024 @cfg {number} g_r radius (pie)
29025 @cfg {number} g_height height of the chart (respected by all elements in the set)
29026 @cfg {number} g_width width of the chart (respected by all elements in the set)
29027 @cfg {Object} title The title of the chart
29030 -opts (object) options for the chart
29032 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29033 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29035 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.
29036 o stacked (boolean) whether or not to tread values as in a stacked bar chart
29038 o stretch (boolean)
29040 -opts (object) options for the pie
29043 o startAngle (number)
29044 o endAngle (number)
29048 * Create a new Input
29049 * @param {Object} config The config object
29052 Roo.bootstrap.Graph = function(config){
29053 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29059 * The img click event for the img.
29060 * @param {Roo.EventObject} e
29066 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
29077 //g_colors: this.colors,
29084 getAutoCreate : function(){
29095 onRender : function(ct,position){
29098 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29100 if (typeof(Raphael) == 'undefined') {
29101 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29105 this.raphael = Raphael(this.el.dom);
29107 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29108 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29109 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29110 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29112 r.text(160, 10, "Single Series Chart").attr(txtattr);
29113 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29114 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29115 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29117 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29118 r.barchart(330, 10, 300, 220, data1);
29119 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29120 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29123 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29124 // r.barchart(30, 30, 560, 250, xdata, {
29125 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29126 // axis : "0 0 1 1",
29127 // axisxlabels : xdata
29128 // //yvalues : cols,
29131 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29133 // this.load(null,xdata,{
29134 // axis : "0 0 1 1",
29135 // axisxlabels : xdata
29140 load : function(graphtype,xdata,opts)
29142 this.raphael.clear();
29144 graphtype = this.graphtype;
29149 var r = this.raphael,
29150 fin = function () {
29151 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29153 fout = function () {
29154 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29156 pfin = function() {
29157 this.sector.stop();
29158 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29161 this.label[0].stop();
29162 this.label[0].attr({ r: 7.5 });
29163 this.label[1].attr({ "font-weight": 800 });
29166 pfout = function() {
29167 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29170 this.label[0].animate({ r: 5 }, 500, "bounce");
29171 this.label[1].attr({ "font-weight": 400 });
29177 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29180 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29183 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
29184 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29186 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29193 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29198 setTitle: function(o)
29203 initEvents: function() {
29206 this.el.on('click', this.onClick, this);
29210 onClick : function(e)
29212 Roo.log('img onclick');
29213 this.fireEvent('click', this, e);
29225 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29228 * @class Roo.bootstrap.dash.NumberBox
29229 * @extends Roo.bootstrap.Component
29230 * Bootstrap NumberBox class
29231 * @cfg {String} headline Box headline
29232 * @cfg {String} content Box content
29233 * @cfg {String} icon Box icon
29234 * @cfg {String} footer Footer text
29235 * @cfg {String} fhref Footer href
29238 * Create a new NumberBox
29239 * @param {Object} config The config object
29243 Roo.bootstrap.dash.NumberBox = function(config){
29244 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29248 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
29257 getAutoCreate : function(){
29261 cls : 'small-box ',
29269 cls : 'roo-headline',
29270 html : this.headline
29274 cls : 'roo-content',
29275 html : this.content
29289 cls : 'ion ' + this.icon
29298 cls : 'small-box-footer',
29299 href : this.fhref || '#',
29303 cfg.cn.push(footer);
29310 onRender : function(ct,position){
29311 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29318 setHeadline: function (value)
29320 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29323 setFooter: function (value, href)
29325 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29328 this.el.select('a.small-box-footer',true).first().attr('href', href);
29333 setContent: function (value)
29335 this.el.select('.roo-content',true).first().dom.innerHTML = value;
29338 initEvents: function()
29352 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29355 * @class Roo.bootstrap.dash.TabBox
29356 * @extends Roo.bootstrap.Component
29357 * @children Roo.bootstrap.dash.TabPane
29358 * Bootstrap TabBox class
29359 * @cfg {String} title Title of the TabBox
29360 * @cfg {String} icon Icon of the TabBox
29361 * @cfg {Boolean} showtabs (true|false) show the tabs default true
29362 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29365 * Create a new TabBox
29366 * @param {Object} config The config object
29370 Roo.bootstrap.dash.TabBox = function(config){
29371 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29376 * When a pane is added
29377 * @param {Roo.bootstrap.dash.TabPane} pane
29381 * @event activatepane
29382 * When a pane is activated
29383 * @param {Roo.bootstrap.dash.TabPane} pane
29385 "activatepane" : true
29393 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
29398 tabScrollable : false,
29400 getChildContainer : function()
29402 return this.el.select('.tab-content', true).first();
29405 getAutoCreate : function(){
29409 cls: 'pull-left header',
29417 cls: 'fa ' + this.icon
29423 cls: 'nav nav-tabs pull-right',
29429 if(this.tabScrollable){
29436 cls: 'nav nav-tabs pull-right',
29447 cls: 'nav-tabs-custom',
29452 cls: 'tab-content no-padding',
29460 initEvents : function()
29462 //Roo.log('add add pane handler');
29463 this.on('addpane', this.onAddPane, this);
29466 * Updates the box title
29467 * @param {String} html to set the title to.
29469 setTitle : function(value)
29471 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29473 onAddPane : function(pane)
29475 this.panes.push(pane);
29476 //Roo.log('addpane');
29478 // tabs are rendere left to right..
29479 if(!this.showtabs){
29483 var ctr = this.el.select('.nav-tabs', true).first();
29486 var existing = ctr.select('.nav-tab',true);
29487 var qty = existing.getCount();;
29490 var tab = ctr.createChild({
29492 cls : 'nav-tab' + (qty ? '' : ' active'),
29500 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29503 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29505 pane.el.addClass('active');
29510 onTabClick : function(ev,un,ob,pane)
29512 //Roo.log('tab - prev default');
29513 ev.preventDefault();
29516 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29517 pane.tab.addClass('active');
29518 //Roo.log(pane.title);
29519 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29520 // technically we should have a deactivate event.. but maybe add later.
29521 // and it should not de-activate the selected tab...
29522 this.fireEvent('activatepane', pane);
29523 pane.el.addClass('active');
29524 pane.fireEvent('activate');
29529 getActivePane : function()
29532 Roo.each(this.panes, function(p) {
29533 if(p.el.hasClass('active')){
29554 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29556 * @class Roo.bootstrap.TabPane
29557 * @extends Roo.bootstrap.Component
29558 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
29559 * Bootstrap TabPane class
29560 * @cfg {Boolean} active (false | true) Default false
29561 * @cfg {String} title title of panel
29565 * Create a new TabPane
29566 * @param {Object} config The config object
29569 Roo.bootstrap.dash.TabPane = function(config){
29570 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29576 * When a pane is activated
29577 * @param {Roo.bootstrap.dash.TabPane} pane
29584 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29589 // the tabBox that this is attached to.
29592 getAutoCreate : function()
29600 cfg.cls += ' active';
29605 initEvents : function()
29607 //Roo.log('trigger add pane handler');
29608 this.parent().fireEvent('addpane', this)
29612 * Updates the tab title
29613 * @param {String} html to set the title to.
29615 setTitle: function(str)
29621 this.tab.select('a', true).first().dom.innerHTML = str;
29640 * @class Roo.bootstrap.Tooltip
29641 * Bootstrap Tooltip class
29642 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29643 * to determine which dom element triggers the tooltip.
29645 * It needs to add support for additional attributes like tooltip-position
29648 * Create a new Toolti
29649 * @param {Object} config The config object
29652 Roo.bootstrap.Tooltip = function(config){
29653 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29655 this.alignment = Roo.bootstrap.Tooltip.alignment;
29657 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29658 this.alignment = config.alignment;
29663 Roo.apply(Roo.bootstrap.Tooltip, {
29665 * @function init initialize tooltip monitoring.
29669 currentTip : false,
29670 currentRegion : false,
29676 Roo.get(document).on('mouseover', this.enter ,this);
29677 Roo.get(document).on('mouseout', this.leave, this);
29680 this.currentTip = new Roo.bootstrap.Tooltip();
29683 enter : function(ev)
29685 var dom = ev.getTarget();
29687 //Roo.log(['enter',dom]);
29688 var el = Roo.fly(dom);
29689 if (this.currentEl) {
29691 //Roo.log(this.currentEl);
29692 //Roo.log(this.currentEl.contains(dom));
29693 if (this.currentEl == el) {
29696 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29702 if (this.currentTip.el) {
29703 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29707 if(!el || el.dom == document){
29713 if (!el.attr('tooltip')) {
29714 pel = el.findParent("[tooltip]");
29716 bindEl = Roo.get(pel);
29722 // you can not look for children, as if el is the body.. then everythign is the child..
29723 if (!pel && !el.attr('tooltip')) { //
29724 if (!el.select("[tooltip]").elements.length) {
29727 // is the mouse over this child...?
29728 bindEl = el.select("[tooltip]").first();
29729 var xy = ev.getXY();
29730 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29731 //Roo.log("not in region.");
29734 //Roo.log("child element over..");
29737 this.currentEl = el;
29738 this.currentTip.bind(bindEl);
29739 this.currentRegion = Roo.lib.Region.getRegion(dom);
29740 this.currentTip.enter();
29743 leave : function(ev)
29745 var dom = ev.getTarget();
29746 //Roo.log(['leave',dom]);
29747 if (!this.currentEl) {
29752 if (dom != this.currentEl.dom) {
29755 var xy = ev.getXY();
29756 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29759 // only activate leave if mouse cursor is outside... bounding box..
29764 if (this.currentTip) {
29765 this.currentTip.leave();
29767 //Roo.log('clear currentEl');
29768 this.currentEl = false;
29773 'left' : ['r-l', [-2,0], 'right'],
29774 'right' : ['l-r', [2,0], 'left'],
29775 'bottom' : ['t-b', [0,2], 'top'],
29776 'top' : [ 'b-t', [0,-2], 'bottom']
29782 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29787 delay : null, // can be { show : 300 , hide: 500}
29791 hoverState : null, //???
29793 placement : 'bottom',
29797 getAutoCreate : function(){
29804 cls : 'tooltip-arrow arrow'
29807 cls : 'tooltip-inner'
29814 bind : function(el)
29819 initEvents : function()
29821 this.arrowEl = this.el.select('.arrow', true).first();
29822 this.innerEl = this.el.select('.tooltip-inner', true).first();
29825 enter : function () {
29827 if (this.timeout != null) {
29828 clearTimeout(this.timeout);
29831 this.hoverState = 'in';
29832 //Roo.log("enter - show");
29833 if (!this.delay || !this.delay.show) {
29838 this.timeout = setTimeout(function () {
29839 if (_t.hoverState == 'in') {
29842 }, this.delay.show);
29846 clearTimeout(this.timeout);
29848 this.hoverState = 'out';
29849 if (!this.delay || !this.delay.hide) {
29855 this.timeout = setTimeout(function () {
29856 //Roo.log("leave - timeout");
29858 if (_t.hoverState == 'out') {
29860 Roo.bootstrap.Tooltip.currentEl = false;
29865 show : function (msg)
29868 this.render(document.body);
29871 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29873 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29875 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29877 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29878 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29880 var placement = typeof this.placement == 'function' ?
29881 this.placement.call(this, this.el, on_el) :
29884 var autoToken = /\s?auto?\s?/i;
29885 var autoPlace = autoToken.test(placement);
29887 placement = placement.replace(autoToken, '') || 'top';
29891 //this.el.setXY([0,0]);
29893 //this.el.dom.style.display='block';
29895 //this.el.appendTo(on_el);
29897 var p = this.getPosition();
29898 var box = this.el.getBox();
29904 var align = this.alignment[placement];
29906 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29908 if(placement == 'top' || placement == 'bottom'){
29910 placement = 'right';
29913 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29914 placement = 'left';
29917 var scroll = Roo.select('body', true).first().getScroll();
29919 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29923 align = this.alignment[placement];
29925 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29929 var elems = document.getElementsByTagName('div');
29930 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29931 for (var i = 0; i < elems.length; i++) {
29932 var zindex = Number.parseInt(
29933 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29936 if (zindex > highest) {
29943 this.el.dom.style.zIndex = highest;
29945 this.el.alignTo(this.bindEl, align[0],align[1]);
29946 //var arrow = this.el.select('.arrow',true).first();
29947 //arrow.set(align[2],
29949 this.el.addClass(placement);
29950 this.el.addClass("bs-tooltip-"+ placement);
29952 this.el.addClass('in fade show');
29954 this.hoverState = null;
29956 if (this.el.hasClass('fade')) {
29971 //this.el.setXY([0,0]);
29972 this.el.removeClass(['show', 'in']);
29988 * @class Roo.bootstrap.LocationPicker
29989 * @extends Roo.bootstrap.Component
29990 * Bootstrap LocationPicker class
29991 * @cfg {Number} latitude Position when init default 0
29992 * @cfg {Number} longitude Position when init default 0
29993 * @cfg {Number} zoom default 15
29994 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29995 * @cfg {Boolean} mapTypeControl default false
29996 * @cfg {Boolean} disableDoubleClickZoom default false
29997 * @cfg {Boolean} scrollwheel default true
29998 * @cfg {Boolean} streetViewControl default false
29999 * @cfg {Number} radius default 0
30000 * @cfg {String} locationName
30001 * @cfg {Boolean} draggable default true
30002 * @cfg {Boolean} enableAutocomplete default false
30003 * @cfg {Boolean} enableReverseGeocode default true
30004 * @cfg {String} markerTitle
30007 * Create a new LocationPicker
30008 * @param {Object} config The config object
30012 Roo.bootstrap.LocationPicker = function(config){
30014 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30019 * Fires when the picker initialized.
30020 * @param {Roo.bootstrap.LocationPicker} this
30021 * @param {Google Location} location
30025 * @event positionchanged
30026 * Fires when the picker position changed.
30027 * @param {Roo.bootstrap.LocationPicker} this
30028 * @param {Google Location} location
30030 positionchanged : true,
30033 * Fires when the map resize.
30034 * @param {Roo.bootstrap.LocationPicker} this
30039 * Fires when the map show.
30040 * @param {Roo.bootstrap.LocationPicker} this
30045 * Fires when the map hide.
30046 * @param {Roo.bootstrap.LocationPicker} this
30051 * Fires when click the map.
30052 * @param {Roo.bootstrap.LocationPicker} this
30053 * @param {Map event} e
30057 * @event mapRightClick
30058 * Fires when right click the map.
30059 * @param {Roo.bootstrap.LocationPicker} this
30060 * @param {Map event} e
30062 mapRightClick : true,
30064 * @event markerClick
30065 * Fires when click the marker.
30066 * @param {Roo.bootstrap.LocationPicker} this
30067 * @param {Map event} e
30069 markerClick : true,
30071 * @event markerRightClick
30072 * Fires when right click the marker.
30073 * @param {Roo.bootstrap.LocationPicker} this
30074 * @param {Map event} e
30076 markerRightClick : true,
30078 * @event OverlayViewDraw
30079 * Fires when OverlayView Draw
30080 * @param {Roo.bootstrap.LocationPicker} this
30082 OverlayViewDraw : true,
30084 * @event OverlayViewOnAdd
30085 * Fires when OverlayView Draw
30086 * @param {Roo.bootstrap.LocationPicker} this
30088 OverlayViewOnAdd : true,
30090 * @event OverlayViewOnRemove
30091 * Fires when OverlayView Draw
30092 * @param {Roo.bootstrap.LocationPicker} this
30094 OverlayViewOnRemove : true,
30096 * @event OverlayViewShow
30097 * Fires when OverlayView Draw
30098 * @param {Roo.bootstrap.LocationPicker} this
30099 * @param {Pixel} cpx
30101 OverlayViewShow : true,
30103 * @event OverlayViewHide
30104 * Fires when OverlayView Draw
30105 * @param {Roo.bootstrap.LocationPicker} this
30107 OverlayViewHide : true,
30109 * @event loadexception
30110 * Fires when load google lib failed.
30111 * @param {Roo.bootstrap.LocationPicker} this
30113 loadexception : true
30118 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30120 gMapContext: false,
30126 mapTypeControl: false,
30127 disableDoubleClickZoom: false,
30129 streetViewControl: false,
30133 enableAutocomplete: false,
30134 enableReverseGeocode: true,
30137 getAutoCreate: function()
30142 cls: 'roo-location-picker'
30148 initEvents: function(ct, position)
30150 if(!this.el.getWidth() || this.isApplied()){
30154 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30159 initial: function()
30161 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30162 this.fireEvent('loadexception', this);
30166 if(!this.mapTypeId){
30167 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30170 this.gMapContext = this.GMapContext();
30172 this.initOverlayView();
30174 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30178 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30179 _this.setPosition(_this.gMapContext.marker.position);
30182 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30183 _this.fireEvent('mapClick', this, event);
30187 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30188 _this.fireEvent('mapRightClick', this, event);
30192 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30193 _this.fireEvent('markerClick', this, event);
30197 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30198 _this.fireEvent('markerRightClick', this, event);
30202 this.setPosition(this.gMapContext.location);
30204 this.fireEvent('initial', this, this.gMapContext.location);
30207 initOverlayView: function()
30211 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30215 _this.fireEvent('OverlayViewDraw', _this);
30220 _this.fireEvent('OverlayViewOnAdd', _this);
30223 onRemove: function()
30225 _this.fireEvent('OverlayViewOnRemove', _this);
30228 show: function(cpx)
30230 _this.fireEvent('OverlayViewShow', _this, cpx);
30235 _this.fireEvent('OverlayViewHide', _this);
30241 fromLatLngToContainerPixel: function(event)
30243 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30246 isApplied: function()
30248 return this.getGmapContext() == false ? false : true;
30251 getGmapContext: function()
30253 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30256 GMapContext: function()
30258 var position = new google.maps.LatLng(this.latitude, this.longitude);
30260 var _map = new google.maps.Map(this.el.dom, {
30263 mapTypeId: this.mapTypeId,
30264 mapTypeControl: this.mapTypeControl,
30265 disableDoubleClickZoom: this.disableDoubleClickZoom,
30266 scrollwheel: this.scrollwheel,
30267 streetViewControl: this.streetViewControl,
30268 locationName: this.locationName,
30269 draggable: this.draggable,
30270 enableAutocomplete: this.enableAutocomplete,
30271 enableReverseGeocode: this.enableReverseGeocode
30274 var _marker = new google.maps.Marker({
30275 position: position,
30277 title: this.markerTitle,
30278 draggable: this.draggable
30285 location: position,
30286 radius: this.radius,
30287 locationName: this.locationName,
30288 addressComponents: {
30289 formatted_address: null,
30290 addressLine1: null,
30291 addressLine2: null,
30293 streetNumber: null,
30297 stateOrProvince: null
30300 domContainer: this.el.dom,
30301 geodecoder: new google.maps.Geocoder()
30305 drawCircle: function(center, radius, options)
30307 if (this.gMapContext.circle != null) {
30308 this.gMapContext.circle.setMap(null);
30312 options = Roo.apply({}, options, {
30313 strokeColor: "#0000FF",
30314 strokeOpacity: .35,
30316 fillColor: "#0000FF",
30320 options.map = this.gMapContext.map;
30321 options.radius = radius;
30322 options.center = center;
30323 this.gMapContext.circle = new google.maps.Circle(options);
30324 return this.gMapContext.circle;
30330 setPosition: function(location)
30332 this.gMapContext.location = location;
30333 this.gMapContext.marker.setPosition(location);
30334 this.gMapContext.map.panTo(location);
30335 this.drawCircle(location, this.gMapContext.radius, {});
30339 if (this.gMapContext.settings.enableReverseGeocode) {
30340 this.gMapContext.geodecoder.geocode({
30341 latLng: this.gMapContext.location
30342 }, function(results, status) {
30344 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30345 _this.gMapContext.locationName = results[0].formatted_address;
30346 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30348 _this.fireEvent('positionchanged', this, location);
30355 this.fireEvent('positionchanged', this, location);
30360 google.maps.event.trigger(this.gMapContext.map, "resize");
30362 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30364 this.fireEvent('resize', this);
30367 setPositionByLatLng: function(latitude, longitude)
30369 this.setPosition(new google.maps.LatLng(latitude, longitude));
30372 getCurrentPosition: function()
30375 latitude: this.gMapContext.location.lat(),
30376 longitude: this.gMapContext.location.lng()
30380 getAddressName: function()
30382 return this.gMapContext.locationName;
30385 getAddressComponents: function()
30387 return this.gMapContext.addressComponents;
30390 address_component_from_google_geocode: function(address_components)
30394 for (var i = 0; i < address_components.length; i++) {
30395 var component = address_components[i];
30396 if (component.types.indexOf("postal_code") >= 0) {
30397 result.postalCode = component.short_name;
30398 } else if (component.types.indexOf("street_number") >= 0) {
30399 result.streetNumber = component.short_name;
30400 } else if (component.types.indexOf("route") >= 0) {
30401 result.streetName = component.short_name;
30402 } else if (component.types.indexOf("neighborhood") >= 0) {
30403 result.city = component.short_name;
30404 } else if (component.types.indexOf("locality") >= 0) {
30405 result.city = component.short_name;
30406 } else if (component.types.indexOf("sublocality") >= 0) {
30407 result.district = component.short_name;
30408 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30409 result.stateOrProvince = component.short_name;
30410 } else if (component.types.indexOf("country") >= 0) {
30411 result.country = component.short_name;
30415 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30416 result.addressLine2 = "";
30420 setZoomLevel: function(zoom)
30422 this.gMapContext.map.setZoom(zoom);
30435 this.fireEvent('show', this);
30446 this.fireEvent('hide', this);
30451 Roo.apply(Roo.bootstrap.LocationPicker, {
30453 OverlayView : function(map, options)
30455 options = options || {};
30462 * @class Roo.bootstrap.Alert
30463 * @extends Roo.bootstrap.Component
30464 * Bootstrap Alert class - shows an alert area box
30466 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30467 Enter a valid email address
30470 * @cfg {String} title The title of alert
30471 * @cfg {String} html The content of alert
30472 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30473 * @cfg {String} fa font-awesomeicon
30474 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30475 * @cfg {Boolean} close true to show a x closer
30479 * Create a new alert
30480 * @param {Object} config The config object
30484 Roo.bootstrap.Alert = function(config){
30485 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30489 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30495 faicon: false, // BC
30499 getAutoCreate : function()
30511 style : this.close ? '' : 'display:none'
30515 cls : 'roo-alert-icon'
30520 cls : 'roo-alert-title',
30525 cls : 'roo-alert-text',
30532 cfg.cn[0].cls += ' fa ' + this.faicon;
30535 cfg.cn[0].cls += ' fa ' + this.fa;
30539 cfg.cls += ' alert-' + this.weight;
30545 initEvents: function()
30547 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30548 this.titleEl = this.el.select('.roo-alert-title',true).first();
30549 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30550 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30551 if (this.seconds > 0) {
30552 this.hide.defer(this.seconds, this);
30556 * Set the Title Message HTML
30557 * @param {String} html
30559 setTitle : function(str)
30561 this.titleEl.dom.innerHTML = str;
30565 * Set the Body Message HTML
30566 * @param {String} html
30568 setHtml : function(str)
30570 this.htmlEl.dom.innerHTML = str;
30573 * Set the Weight of the alert
30574 * @param {String} (success|info|warning|danger) weight
30577 setWeight : function(weight)
30580 this.el.removeClass('alert-' + this.weight);
30583 this.weight = weight;
30585 this.el.addClass('alert-' + this.weight);
30588 * Set the Icon of the alert
30589 * @param {String} see fontawsome names (name without the 'fa-' bit)
30591 setIcon : function(icon)
30594 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30597 this.faicon = icon;
30599 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30624 * @class Roo.bootstrap.UploadCropbox
30625 * @extends Roo.bootstrap.Component
30626 * Bootstrap UploadCropbox class
30627 * @cfg {String} emptyText show when image has been loaded
30628 * @cfg {String} rotateNotify show when image too small to rotate
30629 * @cfg {Number} errorTimeout default 3000
30630 * @cfg {Number} minWidth default 300
30631 * @cfg {Number} minHeight default 300
30632 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30633 * @cfg {Boolean} isDocument (true|false) default false
30634 * @cfg {String} url action url
30635 * @cfg {String} paramName default 'imageUpload'
30636 * @cfg {String} method default POST
30637 * @cfg {Boolean} loadMask (true|false) default true
30638 * @cfg {Boolean} loadingText default 'Loading...'
30641 * Create a new UploadCropbox
30642 * @param {Object} config The config object
30645 Roo.bootstrap.UploadCropbox = function(config){
30646 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30650 * @event beforeselectfile
30651 * Fire before select file
30652 * @param {Roo.bootstrap.UploadCropbox} this
30654 "beforeselectfile" : true,
30657 * Fire after initEvent
30658 * @param {Roo.bootstrap.UploadCropbox} this
30663 * Fire after initEvent
30664 * @param {Roo.bootstrap.UploadCropbox} this
30665 * @param {String} data
30670 * Fire when preparing the file data
30671 * @param {Roo.bootstrap.UploadCropbox} this
30672 * @param {Object} file
30677 * Fire when get exception
30678 * @param {Roo.bootstrap.UploadCropbox} this
30679 * @param {XMLHttpRequest} xhr
30681 "exception" : true,
30683 * @event beforeloadcanvas
30684 * Fire before load the canvas
30685 * @param {Roo.bootstrap.UploadCropbox} this
30686 * @param {String} src
30688 "beforeloadcanvas" : true,
30691 * Fire when trash image
30692 * @param {Roo.bootstrap.UploadCropbox} this
30697 * Fire when download the image
30698 * @param {Roo.bootstrap.UploadCropbox} this
30702 * @event footerbuttonclick
30703 * Fire when footerbuttonclick
30704 * @param {Roo.bootstrap.UploadCropbox} this
30705 * @param {String} type
30707 "footerbuttonclick" : true,
30711 * @param {Roo.bootstrap.UploadCropbox} this
30716 * Fire when rotate the image
30717 * @param {Roo.bootstrap.UploadCropbox} this
30718 * @param {String} pos
30723 * Fire when inspect the file
30724 * @param {Roo.bootstrap.UploadCropbox} this
30725 * @param {Object} file
30730 * Fire when xhr upload the file
30731 * @param {Roo.bootstrap.UploadCropbox} this
30732 * @param {Object} data
30737 * Fire when arrange the file data
30738 * @param {Roo.bootstrap.UploadCropbox} this
30739 * @param {Object} formData
30744 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30747 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30749 emptyText : 'Click to upload image',
30750 rotateNotify : 'Image is too small to rotate',
30751 errorTimeout : 3000,
30765 cropType : 'image/jpeg',
30767 canvasLoaded : false,
30768 isDocument : false,
30770 paramName : 'imageUpload',
30772 loadingText : 'Loading...',
30775 getAutoCreate : function()
30779 cls : 'roo-upload-cropbox',
30783 cls : 'roo-upload-cropbox-selector',
30788 cls : 'roo-upload-cropbox-body',
30789 style : 'cursor:pointer',
30793 cls : 'roo-upload-cropbox-preview'
30797 cls : 'roo-upload-cropbox-thumb'
30801 cls : 'roo-upload-cropbox-empty-notify',
30802 html : this.emptyText
30806 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30807 html : this.rotateNotify
30813 cls : 'roo-upload-cropbox-footer',
30816 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30826 onRender : function(ct, position)
30828 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30830 if (this.buttons.length) {
30832 Roo.each(this.buttons, function(bb) {
30834 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30836 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30842 this.maskEl = this.el;
30846 initEvents : function()
30848 this.urlAPI = (window.createObjectURL && window) ||
30849 (window.URL && URL.revokeObjectURL && URL) ||
30850 (window.webkitURL && webkitURL);
30852 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30853 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30855 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30856 this.selectorEl.hide();
30858 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30859 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30861 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30862 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30863 this.thumbEl.hide();
30865 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30866 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30868 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30869 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30870 this.errorEl.hide();
30872 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30873 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30874 this.footerEl.hide();
30876 this.setThumbBoxSize();
30882 this.fireEvent('initial', this);
30889 window.addEventListener("resize", function() { _this.resize(); } );
30891 this.bodyEl.on('click', this.beforeSelectFile, this);
30894 this.bodyEl.on('touchstart', this.onTouchStart, this);
30895 this.bodyEl.on('touchmove', this.onTouchMove, this);
30896 this.bodyEl.on('touchend', this.onTouchEnd, this);
30900 this.bodyEl.on('mousedown', this.onMouseDown, this);
30901 this.bodyEl.on('mousemove', this.onMouseMove, this);
30902 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30903 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30904 Roo.get(document).on('mouseup', this.onMouseUp, this);
30907 this.selectorEl.on('change', this.onFileSelected, this);
30913 this.baseScale = 1;
30915 this.baseRotate = 1;
30916 this.dragable = false;
30917 this.pinching = false;
30920 this.cropData = false;
30921 this.notifyEl.dom.innerHTML = this.emptyText;
30923 this.selectorEl.dom.value = '';
30927 resize : function()
30929 if(this.fireEvent('resize', this) != false){
30930 this.setThumbBoxPosition();
30931 this.setCanvasPosition();
30935 onFooterButtonClick : function(e, el, o, type)
30938 case 'rotate-left' :
30939 this.onRotateLeft(e);
30941 case 'rotate-right' :
30942 this.onRotateRight(e);
30945 this.beforeSelectFile(e);
30960 this.fireEvent('footerbuttonclick', this, type);
30963 beforeSelectFile : function(e)
30965 e.preventDefault();
30967 if(this.fireEvent('beforeselectfile', this) != false){
30968 this.selectorEl.dom.click();
30972 onFileSelected : function(e)
30974 e.preventDefault();
30976 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30980 var file = this.selectorEl.dom.files[0];
30982 if(this.fireEvent('inspect', this, file) != false){
30983 this.prepare(file);
30988 trash : function(e)
30990 this.fireEvent('trash', this);
30993 download : function(e)
30995 this.fireEvent('download', this);
30998 loadCanvas : function(src)
31000 if(this.fireEvent('beforeloadcanvas', this, src) != false){
31004 this.imageEl = document.createElement('img');
31008 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31010 this.imageEl.src = src;
31014 onLoadCanvas : function()
31016 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31017 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31019 this.bodyEl.un('click', this.beforeSelectFile, this);
31021 this.notifyEl.hide();
31022 this.thumbEl.show();
31023 this.footerEl.show();
31025 this.baseRotateLevel();
31027 if(this.isDocument){
31028 this.setThumbBoxSize();
31031 this.setThumbBoxPosition();
31033 this.baseScaleLevel();
31039 this.canvasLoaded = true;
31042 this.maskEl.unmask();
31047 setCanvasPosition : function()
31049 if(!this.canvasEl){
31053 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31054 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31056 this.previewEl.setLeft(pw);
31057 this.previewEl.setTop(ph);
31061 onMouseDown : function(e)
31065 this.dragable = true;
31066 this.pinching = false;
31068 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31069 this.dragable = false;
31073 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31074 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31078 onMouseMove : function(e)
31082 if(!this.canvasLoaded){
31086 if (!this.dragable){
31090 var minX = Math.ceil(this.thumbEl.getLeft(true));
31091 var minY = Math.ceil(this.thumbEl.getTop(true));
31093 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31094 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31096 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31097 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31099 x = x - this.mouseX;
31100 y = y - this.mouseY;
31102 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31103 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31105 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31106 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31108 this.previewEl.setLeft(bgX);
31109 this.previewEl.setTop(bgY);
31111 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31112 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31115 onMouseUp : function(e)
31119 this.dragable = false;
31122 onMouseWheel : function(e)
31126 this.startScale = this.scale;
31128 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31130 if(!this.zoomable()){
31131 this.scale = this.startScale;
31140 zoomable : function()
31142 var minScale = this.thumbEl.getWidth() / this.minWidth;
31144 if(this.minWidth < this.minHeight){
31145 minScale = this.thumbEl.getHeight() / this.minHeight;
31148 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31149 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31153 (this.rotate == 0 || this.rotate == 180) &&
31155 width > this.imageEl.OriginWidth ||
31156 height > this.imageEl.OriginHeight ||
31157 (width < this.minWidth && height < this.minHeight)
31165 (this.rotate == 90 || this.rotate == 270) &&
31167 width > this.imageEl.OriginWidth ||
31168 height > this.imageEl.OriginHeight ||
31169 (width < this.minHeight && height < this.minWidth)
31176 !this.isDocument &&
31177 (this.rotate == 0 || this.rotate == 180) &&
31179 width < this.minWidth ||
31180 width > this.imageEl.OriginWidth ||
31181 height < this.minHeight ||
31182 height > this.imageEl.OriginHeight
31189 !this.isDocument &&
31190 (this.rotate == 90 || this.rotate == 270) &&
31192 width < this.minHeight ||
31193 width > this.imageEl.OriginWidth ||
31194 height < this.minWidth ||
31195 height > this.imageEl.OriginHeight
31205 onRotateLeft : function(e)
31207 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31209 var minScale = this.thumbEl.getWidth() / this.minWidth;
31211 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31212 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31214 this.startScale = this.scale;
31216 while (this.getScaleLevel() < minScale){
31218 this.scale = this.scale + 1;
31220 if(!this.zoomable()){
31225 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31226 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31231 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31238 this.scale = this.startScale;
31240 this.onRotateFail();
31245 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31247 if(this.isDocument){
31248 this.setThumbBoxSize();
31249 this.setThumbBoxPosition();
31250 this.setCanvasPosition();
31255 this.fireEvent('rotate', this, 'left');
31259 onRotateRight : function(e)
31261 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31263 var minScale = this.thumbEl.getWidth() / this.minWidth;
31265 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31266 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31268 this.startScale = this.scale;
31270 while (this.getScaleLevel() < minScale){
31272 this.scale = this.scale + 1;
31274 if(!this.zoomable()){
31279 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31280 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31285 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31292 this.scale = this.startScale;
31294 this.onRotateFail();
31299 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31301 if(this.isDocument){
31302 this.setThumbBoxSize();
31303 this.setThumbBoxPosition();
31304 this.setCanvasPosition();
31309 this.fireEvent('rotate', this, 'right');
31312 onRotateFail : function()
31314 this.errorEl.show(true);
31318 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31323 this.previewEl.dom.innerHTML = '';
31325 var canvasEl = document.createElement("canvas");
31327 var contextEl = canvasEl.getContext("2d");
31329 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31330 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31331 var center = this.imageEl.OriginWidth / 2;
31333 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31334 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31335 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31336 center = this.imageEl.OriginHeight / 2;
31339 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31341 contextEl.translate(center, center);
31342 contextEl.rotate(this.rotate * Math.PI / 180);
31344 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31346 this.canvasEl = document.createElement("canvas");
31348 this.contextEl = this.canvasEl.getContext("2d");
31350 switch (this.rotate) {
31353 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31354 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31356 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31361 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31362 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31364 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31365 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);
31369 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31374 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31375 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31377 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31378 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);
31382 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);
31387 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31388 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31390 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31391 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31395 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);
31402 this.previewEl.appendChild(this.canvasEl);
31404 this.setCanvasPosition();
31409 if(!this.canvasLoaded){
31413 var imageCanvas = document.createElement("canvas");
31415 var imageContext = imageCanvas.getContext("2d");
31417 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31418 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31420 var center = imageCanvas.width / 2;
31422 imageContext.translate(center, center);
31424 imageContext.rotate(this.rotate * Math.PI / 180);
31426 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31428 var canvas = document.createElement("canvas");
31430 var context = canvas.getContext("2d");
31432 canvas.width = this.minWidth;
31433 canvas.height = this.minHeight;
31435 switch (this.rotate) {
31438 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31439 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31441 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31442 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31444 var targetWidth = this.minWidth - 2 * x;
31445 var targetHeight = this.minHeight - 2 * y;
31449 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31450 scale = targetWidth / width;
31453 if(x > 0 && y == 0){
31454 scale = targetHeight / height;
31457 if(x > 0 && y > 0){
31458 scale = targetWidth / width;
31460 if(width < height){
31461 scale = targetHeight / height;
31465 context.scale(scale, scale);
31467 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31468 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31470 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31471 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31473 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31478 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31479 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31481 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31482 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31484 var targetWidth = this.minWidth - 2 * x;
31485 var targetHeight = this.minHeight - 2 * y;
31489 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31490 scale = targetWidth / width;
31493 if(x > 0 && y == 0){
31494 scale = targetHeight / height;
31497 if(x > 0 && y > 0){
31498 scale = targetWidth / width;
31500 if(width < height){
31501 scale = targetHeight / height;
31505 context.scale(scale, scale);
31507 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31508 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31510 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31511 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31513 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31515 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31520 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31521 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31523 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31524 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31526 var targetWidth = this.minWidth - 2 * x;
31527 var targetHeight = this.minHeight - 2 * y;
31531 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31532 scale = targetWidth / width;
31535 if(x > 0 && y == 0){
31536 scale = targetHeight / height;
31539 if(x > 0 && y > 0){
31540 scale = targetWidth / width;
31542 if(width < height){
31543 scale = targetHeight / height;
31547 context.scale(scale, scale);
31549 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31550 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31552 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31553 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31555 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31556 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31558 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31563 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31564 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31566 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31567 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31569 var targetWidth = this.minWidth - 2 * x;
31570 var targetHeight = this.minHeight - 2 * y;
31574 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31575 scale = targetWidth / width;
31578 if(x > 0 && y == 0){
31579 scale = targetHeight / height;
31582 if(x > 0 && y > 0){
31583 scale = targetWidth / width;
31585 if(width < height){
31586 scale = targetHeight / height;
31590 context.scale(scale, scale);
31592 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31593 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31595 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31596 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31598 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31600 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31607 this.cropData = canvas.toDataURL(this.cropType);
31609 if(this.fireEvent('crop', this, this.cropData) !== false){
31610 this.process(this.file, this.cropData);
31617 setThumbBoxSize : function()
31621 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31622 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31623 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31625 this.minWidth = width;
31626 this.minHeight = height;
31628 if(this.rotate == 90 || this.rotate == 270){
31629 this.minWidth = height;
31630 this.minHeight = width;
31635 width = Math.ceil(this.minWidth * height / this.minHeight);
31637 if(this.minWidth > this.minHeight){
31639 height = Math.ceil(this.minHeight * width / this.minWidth);
31642 this.thumbEl.setStyle({
31643 width : width + 'px',
31644 height : height + 'px'
31651 setThumbBoxPosition : function()
31653 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31654 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31656 this.thumbEl.setLeft(x);
31657 this.thumbEl.setTop(y);
31661 baseRotateLevel : function()
31663 this.baseRotate = 1;
31666 typeof(this.exif) != 'undefined' &&
31667 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31668 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31670 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31673 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31677 baseScaleLevel : function()
31681 if(this.isDocument){
31683 if(this.baseRotate == 6 || this.baseRotate == 8){
31685 height = this.thumbEl.getHeight();
31686 this.baseScale = height / this.imageEl.OriginWidth;
31688 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31689 width = this.thumbEl.getWidth();
31690 this.baseScale = width / this.imageEl.OriginHeight;
31696 height = this.thumbEl.getHeight();
31697 this.baseScale = height / this.imageEl.OriginHeight;
31699 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31700 width = this.thumbEl.getWidth();
31701 this.baseScale = width / this.imageEl.OriginWidth;
31707 if(this.baseRotate == 6 || this.baseRotate == 8){
31709 width = this.thumbEl.getHeight();
31710 this.baseScale = width / this.imageEl.OriginHeight;
31712 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31713 height = this.thumbEl.getWidth();
31714 this.baseScale = height / this.imageEl.OriginHeight;
31717 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31718 height = this.thumbEl.getWidth();
31719 this.baseScale = height / this.imageEl.OriginHeight;
31721 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31722 width = this.thumbEl.getHeight();
31723 this.baseScale = width / this.imageEl.OriginWidth;
31730 width = this.thumbEl.getWidth();
31731 this.baseScale = width / this.imageEl.OriginWidth;
31733 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31734 height = this.thumbEl.getHeight();
31735 this.baseScale = height / this.imageEl.OriginHeight;
31738 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31740 height = this.thumbEl.getHeight();
31741 this.baseScale = height / this.imageEl.OriginHeight;
31743 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31744 width = this.thumbEl.getWidth();
31745 this.baseScale = width / this.imageEl.OriginWidth;
31753 getScaleLevel : function()
31755 return this.baseScale * Math.pow(1.1, this.scale);
31758 onTouchStart : function(e)
31760 if(!this.canvasLoaded){
31761 this.beforeSelectFile(e);
31765 var touches = e.browserEvent.touches;
31771 if(touches.length == 1){
31772 this.onMouseDown(e);
31776 if(touches.length != 2){
31782 for(var i = 0, finger; finger = touches[i]; i++){
31783 coords.push(finger.pageX, finger.pageY);
31786 var x = Math.pow(coords[0] - coords[2], 2);
31787 var y = Math.pow(coords[1] - coords[3], 2);
31789 this.startDistance = Math.sqrt(x + y);
31791 this.startScale = this.scale;
31793 this.pinching = true;
31794 this.dragable = false;
31798 onTouchMove : function(e)
31800 if(!this.pinching && !this.dragable){
31804 var touches = e.browserEvent.touches;
31811 this.onMouseMove(e);
31817 for(var i = 0, finger; finger = touches[i]; i++){
31818 coords.push(finger.pageX, finger.pageY);
31821 var x = Math.pow(coords[0] - coords[2], 2);
31822 var y = Math.pow(coords[1] - coords[3], 2);
31824 this.endDistance = Math.sqrt(x + y);
31826 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31828 if(!this.zoomable()){
31829 this.scale = this.startScale;
31837 onTouchEnd : function(e)
31839 this.pinching = false;
31840 this.dragable = false;
31844 process : function(file, crop)
31847 this.maskEl.mask(this.loadingText);
31850 this.xhr = new XMLHttpRequest();
31852 file.xhr = this.xhr;
31854 this.xhr.open(this.method, this.url, true);
31857 "Accept": "application/json",
31858 "Cache-Control": "no-cache",
31859 "X-Requested-With": "XMLHttpRequest"
31862 for (var headerName in headers) {
31863 var headerValue = headers[headerName];
31865 this.xhr.setRequestHeader(headerName, headerValue);
31871 this.xhr.onload = function()
31873 _this.xhrOnLoad(_this.xhr);
31876 this.xhr.onerror = function()
31878 _this.xhrOnError(_this.xhr);
31881 var formData = new FormData();
31883 formData.append('returnHTML', 'NO');
31886 formData.append('crop', crop);
31889 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31890 formData.append(this.paramName, file, file.name);
31893 if(typeof(file.filename) != 'undefined'){
31894 formData.append('filename', file.filename);
31897 if(typeof(file.mimetype) != 'undefined'){
31898 formData.append('mimetype', file.mimetype);
31901 if(this.fireEvent('arrange', this, formData) != false){
31902 this.xhr.send(formData);
31906 xhrOnLoad : function(xhr)
31909 this.maskEl.unmask();
31912 if (xhr.readyState !== 4) {
31913 this.fireEvent('exception', this, xhr);
31917 var response = Roo.decode(xhr.responseText);
31919 if(!response.success){
31920 this.fireEvent('exception', this, xhr);
31924 var response = Roo.decode(xhr.responseText);
31926 this.fireEvent('upload', this, response);
31930 xhrOnError : function()
31933 this.maskEl.unmask();
31936 Roo.log('xhr on error');
31938 var response = Roo.decode(xhr.responseText);
31944 prepare : function(file)
31947 this.maskEl.mask(this.loadingText);
31953 if(typeof(file) === 'string'){
31954 this.loadCanvas(file);
31958 if(!file || !this.urlAPI){
31963 this.cropType = file.type;
31967 if(this.fireEvent('prepare', this, this.file) != false){
31969 var reader = new FileReader();
31971 reader.onload = function (e) {
31972 if (e.target.error) {
31973 Roo.log(e.target.error);
31977 var buffer = e.target.result,
31978 dataView = new DataView(buffer),
31980 maxOffset = dataView.byteLength - 4,
31984 if (dataView.getUint16(0) === 0xffd8) {
31985 while (offset < maxOffset) {
31986 markerBytes = dataView.getUint16(offset);
31988 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31989 markerLength = dataView.getUint16(offset + 2) + 2;
31990 if (offset + markerLength > dataView.byteLength) {
31991 Roo.log('Invalid meta data: Invalid segment size.');
31995 if(markerBytes == 0xffe1){
31996 _this.parseExifData(
32003 offset += markerLength;
32013 var url = _this.urlAPI.createObjectURL(_this.file);
32015 _this.loadCanvas(url);
32020 reader.readAsArrayBuffer(this.file);
32026 parseExifData : function(dataView, offset, length)
32028 var tiffOffset = offset + 10,
32032 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32033 // No Exif data, might be XMP data instead
32037 // Check for the ASCII code for "Exif" (0x45786966):
32038 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32039 // No Exif data, might be XMP data instead
32042 if (tiffOffset + 8 > dataView.byteLength) {
32043 Roo.log('Invalid Exif data: Invalid segment size.');
32046 // Check for the two null bytes:
32047 if (dataView.getUint16(offset + 8) !== 0x0000) {
32048 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32051 // Check the byte alignment:
32052 switch (dataView.getUint16(tiffOffset)) {
32054 littleEndian = true;
32057 littleEndian = false;
32060 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32063 // Check for the TIFF tag marker (0x002A):
32064 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32065 Roo.log('Invalid Exif data: Missing TIFF marker.');
32068 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32069 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32071 this.parseExifTags(
32074 tiffOffset + dirOffset,
32079 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32084 if (dirOffset + 6 > dataView.byteLength) {
32085 Roo.log('Invalid Exif data: Invalid directory offset.');
32088 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32089 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32090 if (dirEndOffset + 4 > dataView.byteLength) {
32091 Roo.log('Invalid Exif data: Invalid directory size.');
32094 for (i = 0; i < tagsNumber; i += 1) {
32098 dirOffset + 2 + 12 * i, // tag offset
32102 // Return the offset to the next directory:
32103 return dataView.getUint32(dirEndOffset, littleEndian);
32106 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32108 var tag = dataView.getUint16(offset, littleEndian);
32110 this.exif[tag] = this.getExifValue(
32114 dataView.getUint16(offset + 2, littleEndian), // tag type
32115 dataView.getUint32(offset + 4, littleEndian), // tag length
32120 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32122 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32131 Roo.log('Invalid Exif data: Invalid tag type.');
32135 tagSize = tagType.size * length;
32136 // Determine if the value is contained in the dataOffset bytes,
32137 // or if the value at the dataOffset is a pointer to the actual data:
32138 dataOffset = tagSize > 4 ?
32139 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32140 if (dataOffset + tagSize > dataView.byteLength) {
32141 Roo.log('Invalid Exif data: Invalid data offset.');
32144 if (length === 1) {
32145 return tagType.getValue(dataView, dataOffset, littleEndian);
32148 for (i = 0; i < length; i += 1) {
32149 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32152 if (tagType.ascii) {
32154 // Concatenate the chars:
32155 for (i = 0; i < values.length; i += 1) {
32157 // Ignore the terminating NULL byte(s):
32158 if (c === '\u0000') {
32170 Roo.apply(Roo.bootstrap.UploadCropbox, {
32172 'Orientation': 0x0112
32176 1: 0, //'top-left',
32178 3: 180, //'bottom-right',
32179 // 4: 'bottom-left',
32181 6: 90, //'right-top',
32182 // 7: 'right-bottom',
32183 8: 270 //'left-bottom'
32187 // byte, 8-bit unsigned int:
32189 getValue: function (dataView, dataOffset) {
32190 return dataView.getUint8(dataOffset);
32194 // ascii, 8-bit byte:
32196 getValue: function (dataView, dataOffset) {
32197 return String.fromCharCode(dataView.getUint8(dataOffset));
32202 // short, 16 bit int:
32204 getValue: function (dataView, dataOffset, littleEndian) {
32205 return dataView.getUint16(dataOffset, littleEndian);
32209 // long, 32 bit int:
32211 getValue: function (dataView, dataOffset, littleEndian) {
32212 return dataView.getUint32(dataOffset, littleEndian);
32216 // rational = two long values, first is numerator, second is denominator:
32218 getValue: function (dataView, dataOffset, littleEndian) {
32219 return dataView.getUint32(dataOffset, littleEndian) /
32220 dataView.getUint32(dataOffset + 4, littleEndian);
32224 // slong, 32 bit signed int:
32226 getValue: function (dataView, dataOffset, littleEndian) {
32227 return dataView.getInt32(dataOffset, littleEndian);
32231 // srational, two slongs, first is numerator, second is denominator:
32233 getValue: function (dataView, dataOffset, littleEndian) {
32234 return dataView.getInt32(dataOffset, littleEndian) /
32235 dataView.getInt32(dataOffset + 4, littleEndian);
32245 cls : 'btn-group roo-upload-cropbox-rotate-left',
32246 action : 'rotate-left',
32250 cls : 'btn btn-default',
32251 html : '<i class="fa fa-undo"></i>'
32257 cls : 'btn-group roo-upload-cropbox-picture',
32258 action : 'picture',
32262 cls : 'btn btn-default',
32263 html : '<i class="fa fa-picture-o"></i>'
32269 cls : 'btn-group roo-upload-cropbox-rotate-right',
32270 action : 'rotate-right',
32274 cls : 'btn btn-default',
32275 html : '<i class="fa fa-repeat"></i>'
32283 cls : 'btn-group roo-upload-cropbox-rotate-left',
32284 action : 'rotate-left',
32288 cls : 'btn btn-default',
32289 html : '<i class="fa fa-undo"></i>'
32295 cls : 'btn-group roo-upload-cropbox-download',
32296 action : 'download',
32300 cls : 'btn btn-default',
32301 html : '<i class="fa fa-download"></i>'
32307 cls : 'btn-group roo-upload-cropbox-crop',
32312 cls : 'btn btn-default',
32313 html : '<i class="fa fa-crop"></i>'
32319 cls : 'btn-group roo-upload-cropbox-trash',
32324 cls : 'btn btn-default',
32325 html : '<i class="fa fa-trash"></i>'
32331 cls : 'btn-group roo-upload-cropbox-rotate-right',
32332 action : 'rotate-right',
32336 cls : 'btn btn-default',
32337 html : '<i class="fa fa-repeat"></i>'
32345 cls : 'btn-group roo-upload-cropbox-rotate-left',
32346 action : 'rotate-left',
32350 cls : 'btn btn-default',
32351 html : '<i class="fa fa-undo"></i>'
32357 cls : 'btn-group roo-upload-cropbox-rotate-right',
32358 action : 'rotate-right',
32362 cls : 'btn btn-default',
32363 html : '<i class="fa fa-repeat"></i>'
32376 * @class Roo.bootstrap.DocumentManager
32377 * @extends Roo.bootstrap.Component
32378 * Bootstrap DocumentManager class
32379 * @cfg {String} paramName default 'imageUpload'
32380 * @cfg {String} toolTipName default 'filename'
32381 * @cfg {String} method default POST
32382 * @cfg {String} url action url
32383 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32384 * @cfg {Boolean} multiple multiple upload default true
32385 * @cfg {Number} thumbSize default 300
32386 * @cfg {String} fieldLabel
32387 * @cfg {Number} labelWidth default 4
32388 * @cfg {String} labelAlign (left|top) default left
32389 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32390 * @cfg {Number} labellg set the width of label (1-12)
32391 * @cfg {Number} labelmd set the width of label (1-12)
32392 * @cfg {Number} labelsm set the width of label (1-12)
32393 * @cfg {Number} labelxs set the width of label (1-12)
32396 * Create a new DocumentManager
32397 * @param {Object} config The config object
32400 Roo.bootstrap.DocumentManager = function(config){
32401 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32404 this.delegates = [];
32409 * Fire when initial the DocumentManager
32410 * @param {Roo.bootstrap.DocumentManager} this
32415 * inspect selected file
32416 * @param {Roo.bootstrap.DocumentManager} this
32417 * @param {File} file
32422 * Fire when xhr load exception
32423 * @param {Roo.bootstrap.DocumentManager} this
32424 * @param {XMLHttpRequest} xhr
32426 "exception" : true,
32428 * @event afterupload
32429 * Fire when xhr load exception
32430 * @param {Roo.bootstrap.DocumentManager} this
32431 * @param {XMLHttpRequest} xhr
32433 "afterupload" : true,
32436 * prepare the form data
32437 * @param {Roo.bootstrap.DocumentManager} this
32438 * @param {Object} formData
32443 * Fire when remove the file
32444 * @param {Roo.bootstrap.DocumentManager} this
32445 * @param {Object} file
32450 * Fire after refresh the file
32451 * @param {Roo.bootstrap.DocumentManager} this
32456 * Fire after click the image
32457 * @param {Roo.bootstrap.DocumentManager} this
32458 * @param {Object} file
32463 * Fire when upload a image and editable set to true
32464 * @param {Roo.bootstrap.DocumentManager} this
32465 * @param {Object} file
32469 * @event beforeselectfile
32470 * Fire before select file
32471 * @param {Roo.bootstrap.DocumentManager} this
32473 "beforeselectfile" : true,
32476 * Fire before process file
32477 * @param {Roo.bootstrap.DocumentManager} this
32478 * @param {Object} file
32482 * @event previewrendered
32483 * Fire when preview rendered
32484 * @param {Roo.bootstrap.DocumentManager} this
32485 * @param {Object} file
32487 "previewrendered" : true,
32490 "previewResize" : true
32495 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32504 paramName : 'imageUpload',
32505 toolTipName : 'filename',
32508 labelAlign : 'left',
32518 getAutoCreate : function()
32520 var managerWidget = {
32522 cls : 'roo-document-manager',
32526 cls : 'roo-document-manager-selector',
32531 cls : 'roo-document-manager-uploader',
32535 cls : 'roo-document-manager-upload-btn',
32536 html : '<i class="fa fa-plus"></i>'
32547 cls : 'column col-md-12',
32552 if(this.fieldLabel.length){
32557 cls : 'column col-md-12',
32558 html : this.fieldLabel
32562 cls : 'column col-md-12',
32567 if(this.labelAlign == 'left'){
32572 html : this.fieldLabel
32581 if(this.labelWidth > 12){
32582 content[0].style = "width: " + this.labelWidth + 'px';
32585 if(this.labelWidth < 13 && this.labelmd == 0){
32586 this.labelmd = this.labelWidth;
32589 if(this.labellg > 0){
32590 content[0].cls += ' col-lg-' + this.labellg;
32591 content[1].cls += ' col-lg-' + (12 - this.labellg);
32594 if(this.labelmd > 0){
32595 content[0].cls += ' col-md-' + this.labelmd;
32596 content[1].cls += ' col-md-' + (12 - this.labelmd);
32599 if(this.labelsm > 0){
32600 content[0].cls += ' col-sm-' + this.labelsm;
32601 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32604 if(this.labelxs > 0){
32605 content[0].cls += ' col-xs-' + this.labelxs;
32606 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32614 cls : 'row clearfix',
32622 initEvents : function()
32624 this.managerEl = this.el.select('.roo-document-manager', true).first();
32625 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32627 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32628 this.selectorEl.hide();
32631 this.selectorEl.attr('multiple', 'multiple');
32634 this.selectorEl.on('change', this.onFileSelected, this);
32636 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32637 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32639 this.uploader.on('click', this.onUploaderClick, this);
32641 this.renderProgressDialog();
32645 window.addEventListener("resize", function() { _this.refresh(); } );
32647 this.fireEvent('initial', this);
32650 renderProgressDialog : function()
32654 this.progressDialog = new Roo.bootstrap.Modal({
32655 cls : 'roo-document-manager-progress-dialog',
32656 allow_close : false,
32667 btnclick : function() {
32668 _this.uploadCancel();
32674 this.progressDialog.render(Roo.get(document.body));
32676 this.progress = new Roo.bootstrap.Progress({
32677 cls : 'roo-document-manager-progress',
32682 this.progress.render(this.progressDialog.getChildContainer());
32684 this.progressBar = new Roo.bootstrap.ProgressBar({
32685 cls : 'roo-document-manager-progress-bar',
32688 aria_valuemax : 12,
32692 this.progressBar.render(this.progress.getChildContainer());
32695 onUploaderClick : function(e)
32697 e.preventDefault();
32699 if(this.fireEvent('beforeselectfile', this) != false){
32700 this.selectorEl.dom.click();
32705 onFileSelected : function(e)
32707 e.preventDefault();
32709 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32713 Roo.each(this.selectorEl.dom.files, function(file){
32714 if(this.fireEvent('inspect', this, file) != false){
32715 this.files.push(file);
32725 this.selectorEl.dom.value = '';
32727 if(!this.files || !this.files.length){
32731 if(this.boxes > 0 && this.files.length > this.boxes){
32732 this.files = this.files.slice(0, this.boxes);
32735 this.uploader.show();
32737 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32738 this.uploader.hide();
32747 Roo.each(this.files, function(file){
32749 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32750 var f = this.renderPreview(file);
32755 if(file.type.indexOf('image') != -1){
32756 this.delegates.push(
32758 _this.process(file);
32759 }).createDelegate(this)
32767 _this.process(file);
32768 }).createDelegate(this)
32773 this.files = files;
32775 this.delegates = this.delegates.concat(docs);
32777 if(!this.delegates.length){
32782 this.progressBar.aria_valuemax = this.delegates.length;
32789 arrange : function()
32791 if(!this.delegates.length){
32792 this.progressDialog.hide();
32797 var delegate = this.delegates.shift();
32799 this.progressDialog.show();
32801 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32803 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32808 refresh : function()
32810 this.uploader.show();
32812 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32813 this.uploader.hide();
32816 Roo.isTouch ? this.closable(false) : this.closable(true);
32818 this.fireEvent('refresh', this);
32821 onRemove : function(e, el, o)
32823 e.preventDefault();
32825 this.fireEvent('remove', this, o);
32829 remove : function(o)
32833 Roo.each(this.files, function(file){
32834 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32843 this.files = files;
32850 Roo.each(this.files, function(file){
32855 file.target.remove();
32864 onClick : function(e, el, o)
32866 e.preventDefault();
32868 this.fireEvent('click', this, o);
32872 closable : function(closable)
32874 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32876 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32888 xhrOnLoad : function(xhr)
32890 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32894 if (xhr.readyState !== 4) {
32896 this.fireEvent('exception', this, xhr);
32900 var response = Roo.decode(xhr.responseText);
32902 if(!response.success){
32904 this.fireEvent('exception', this, xhr);
32908 var file = this.renderPreview(response.data);
32910 this.files.push(file);
32914 this.fireEvent('afterupload', this, xhr);
32918 xhrOnError : function(xhr)
32920 Roo.log('xhr on error');
32922 var response = Roo.decode(xhr.responseText);
32929 process : function(file)
32931 if(this.fireEvent('process', this, file) !== false){
32932 if(this.editable && file.type.indexOf('image') != -1){
32933 this.fireEvent('edit', this, file);
32937 this.uploadStart(file, false);
32944 uploadStart : function(file, crop)
32946 this.xhr = new XMLHttpRequest();
32948 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32953 file.xhr = this.xhr;
32955 this.managerEl.createChild({
32957 cls : 'roo-document-manager-loading',
32961 tooltip : file.name,
32962 cls : 'roo-document-manager-thumb',
32963 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32969 this.xhr.open(this.method, this.url, true);
32972 "Accept": "application/json",
32973 "Cache-Control": "no-cache",
32974 "X-Requested-With": "XMLHttpRequest"
32977 for (var headerName in headers) {
32978 var headerValue = headers[headerName];
32980 this.xhr.setRequestHeader(headerName, headerValue);
32986 this.xhr.onload = function()
32988 _this.xhrOnLoad(_this.xhr);
32991 this.xhr.onerror = function()
32993 _this.xhrOnError(_this.xhr);
32996 var formData = new FormData();
32998 formData.append('returnHTML', 'NO');
33001 formData.append('crop', crop);
33004 formData.append(this.paramName, file, file.name);
33011 if(this.fireEvent('prepare', this, formData, options) != false){
33013 if(options.manually){
33017 this.xhr.send(formData);
33021 this.uploadCancel();
33024 uploadCancel : function()
33030 this.delegates = [];
33032 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33039 renderPreview : function(file)
33041 if(typeof(file.target) != 'undefined' && file.target){
33045 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33047 var previewEl = this.managerEl.createChild({
33049 cls : 'roo-document-manager-preview',
33053 tooltip : file[this.toolTipName],
33054 cls : 'roo-document-manager-thumb',
33055 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33060 html : '<i class="fa fa-times-circle"></i>'
33065 var close = previewEl.select('button.close', true).first();
33067 close.on('click', this.onRemove, this, file);
33069 file.target = previewEl;
33071 var image = previewEl.select('img', true).first();
33075 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33077 image.on('click', this.onClick, this, file);
33079 this.fireEvent('previewrendered', this, file);
33085 onPreviewLoad : function(file, image)
33087 if(typeof(file.target) == 'undefined' || !file.target){
33091 var width = image.dom.naturalWidth || image.dom.width;
33092 var height = image.dom.naturalHeight || image.dom.height;
33094 if(!this.previewResize) {
33098 if(width > height){
33099 file.target.addClass('wide');
33103 file.target.addClass('tall');
33108 uploadFromSource : function(file, crop)
33110 this.xhr = new XMLHttpRequest();
33112 this.managerEl.createChild({
33114 cls : 'roo-document-manager-loading',
33118 tooltip : file.name,
33119 cls : 'roo-document-manager-thumb',
33120 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33126 this.xhr.open(this.method, this.url, true);
33129 "Accept": "application/json",
33130 "Cache-Control": "no-cache",
33131 "X-Requested-With": "XMLHttpRequest"
33134 for (var headerName in headers) {
33135 var headerValue = headers[headerName];
33137 this.xhr.setRequestHeader(headerName, headerValue);
33143 this.xhr.onload = function()
33145 _this.xhrOnLoad(_this.xhr);
33148 this.xhr.onerror = function()
33150 _this.xhrOnError(_this.xhr);
33153 var formData = new FormData();
33155 formData.append('returnHTML', 'NO');
33157 formData.append('crop', crop);
33159 if(typeof(file.filename) != 'undefined'){
33160 formData.append('filename', file.filename);
33163 if(typeof(file.mimetype) != 'undefined'){
33164 formData.append('mimetype', file.mimetype);
33169 if(this.fireEvent('prepare', this, formData) != false){
33170 this.xhr.send(formData);
33180 * @class Roo.bootstrap.DocumentViewer
33181 * @extends Roo.bootstrap.Component
33182 * Bootstrap DocumentViewer class
33183 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33184 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33187 * Create a new DocumentViewer
33188 * @param {Object} config The config object
33191 Roo.bootstrap.DocumentViewer = function(config){
33192 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33197 * Fire after initEvent
33198 * @param {Roo.bootstrap.DocumentViewer} this
33204 * @param {Roo.bootstrap.DocumentViewer} this
33209 * Fire after download button
33210 * @param {Roo.bootstrap.DocumentViewer} this
33215 * Fire after trash button
33216 * @param {Roo.bootstrap.DocumentViewer} this
33223 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33225 showDownload : true,
33229 getAutoCreate : function()
33233 cls : 'roo-document-viewer',
33237 cls : 'roo-document-viewer-body',
33241 cls : 'roo-document-viewer-thumb',
33245 cls : 'roo-document-viewer-image'
33253 cls : 'roo-document-viewer-footer',
33256 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33260 cls : 'btn-group roo-document-viewer-download',
33264 cls : 'btn btn-default',
33265 html : '<i class="fa fa-download"></i>'
33271 cls : 'btn-group roo-document-viewer-trash',
33275 cls : 'btn btn-default',
33276 html : '<i class="fa fa-trash"></i>'
33289 initEvents : function()
33291 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33292 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33294 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33295 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33297 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33298 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33300 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33301 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33303 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33304 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33306 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33307 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33309 this.bodyEl.on('click', this.onClick, this);
33310 this.downloadBtn.on('click', this.onDownload, this);
33311 this.trashBtn.on('click', this.onTrash, this);
33313 this.downloadBtn.hide();
33314 this.trashBtn.hide();
33316 if(this.showDownload){
33317 this.downloadBtn.show();
33320 if(this.showTrash){
33321 this.trashBtn.show();
33324 if(!this.showDownload && !this.showTrash) {
33325 this.footerEl.hide();
33330 initial : function()
33332 this.fireEvent('initial', this);
33336 onClick : function(e)
33338 e.preventDefault();
33340 this.fireEvent('click', this);
33343 onDownload : function(e)
33345 e.preventDefault();
33347 this.fireEvent('download', this);
33350 onTrash : function(e)
33352 e.preventDefault();
33354 this.fireEvent('trash', this);
33366 * @class Roo.bootstrap.FieldLabel
33367 * @extends Roo.bootstrap.Component
33368 * Bootstrap FieldLabel class
33369 * @cfg {String} html contents of the element
33370 * @cfg {String} tag tag of the element default label
33371 * @cfg {String} cls class of the element
33372 * @cfg {String} target label target
33373 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33374 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33375 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33376 * @cfg {String} iconTooltip default "This field is required"
33377 * @cfg {String} indicatorpos (left|right) default left
33380 * Create a new FieldLabel
33381 * @param {Object} config The config object
33384 Roo.bootstrap.FieldLabel = function(config){
33385 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33390 * Fires after the field has been marked as invalid.
33391 * @param {Roo.form.FieldLabel} this
33392 * @param {String} msg The validation message
33397 * Fires after the field has been validated with no errors.
33398 * @param {Roo.form.FieldLabel} this
33404 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33411 invalidClass : 'has-warning',
33412 validClass : 'has-success',
33413 iconTooltip : 'This field is required',
33414 indicatorpos : 'left',
33416 getAutoCreate : function(){
33419 if (!this.allowBlank) {
33425 cls : 'roo-bootstrap-field-label ' + this.cls,
33430 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33431 tooltip : this.iconTooltip
33440 if(this.indicatorpos == 'right'){
33443 cls : 'roo-bootstrap-field-label ' + this.cls,
33452 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33453 tooltip : this.iconTooltip
33462 initEvents: function()
33464 Roo.bootstrap.Element.superclass.initEvents.call(this);
33466 this.indicator = this.indicatorEl();
33468 if(this.indicator){
33469 this.indicator.removeClass('visible');
33470 this.indicator.addClass('invisible');
33473 Roo.bootstrap.FieldLabel.register(this);
33476 indicatorEl : function()
33478 var indicator = this.el.select('i.roo-required-indicator',true).first();
33489 * Mark this field as valid
33491 markValid : function()
33493 if(this.indicator){
33494 this.indicator.removeClass('visible');
33495 this.indicator.addClass('invisible');
33497 if (Roo.bootstrap.version == 3) {
33498 this.el.removeClass(this.invalidClass);
33499 this.el.addClass(this.validClass);
33501 this.el.removeClass('is-invalid');
33502 this.el.addClass('is-valid');
33506 this.fireEvent('valid', this);
33510 * Mark this field as invalid
33511 * @param {String} msg The validation message
33513 markInvalid : function(msg)
33515 if(this.indicator){
33516 this.indicator.removeClass('invisible');
33517 this.indicator.addClass('visible');
33519 if (Roo.bootstrap.version == 3) {
33520 this.el.removeClass(this.validClass);
33521 this.el.addClass(this.invalidClass);
33523 this.el.removeClass('is-valid');
33524 this.el.addClass('is-invalid');
33528 this.fireEvent('invalid', this, msg);
33534 Roo.apply(Roo.bootstrap.FieldLabel, {
33539 * register a FieldLabel Group
33540 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33542 register : function(label)
33544 if(this.groups.hasOwnProperty(label.target)){
33548 this.groups[label.target] = label;
33552 * fetch a FieldLabel Group based on the target
33553 * @param {string} target
33554 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33556 get: function(target) {
33557 if (typeof(this.groups[target]) == 'undefined') {
33561 return this.groups[target] ;
33570 * page DateSplitField.
33576 * @class Roo.bootstrap.DateSplitField
33577 * @extends Roo.bootstrap.Component
33578 * Bootstrap DateSplitField class
33579 * @cfg {string} fieldLabel - the label associated
33580 * @cfg {Number} labelWidth set the width of label (0-12)
33581 * @cfg {String} labelAlign (top|left)
33582 * @cfg {Boolean} dayAllowBlank (true|false) default false
33583 * @cfg {Boolean} monthAllowBlank (true|false) default false
33584 * @cfg {Boolean} yearAllowBlank (true|false) default false
33585 * @cfg {string} dayPlaceholder
33586 * @cfg {string} monthPlaceholder
33587 * @cfg {string} yearPlaceholder
33588 * @cfg {string} dayFormat default 'd'
33589 * @cfg {string} monthFormat default 'm'
33590 * @cfg {string} yearFormat default 'Y'
33591 * @cfg {Number} labellg set the width of label (1-12)
33592 * @cfg {Number} labelmd set the width of label (1-12)
33593 * @cfg {Number} labelsm set the width of label (1-12)
33594 * @cfg {Number} labelxs set the width of label (1-12)
33598 * Create a new DateSplitField
33599 * @param {Object} config The config object
33602 Roo.bootstrap.DateSplitField = function(config){
33603 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33609 * getting the data of years
33610 * @param {Roo.bootstrap.DateSplitField} this
33611 * @param {Object} years
33616 * getting the data of days
33617 * @param {Roo.bootstrap.DateSplitField} this
33618 * @param {Object} days
33623 * Fires after the field has been marked as invalid.
33624 * @param {Roo.form.Field} this
33625 * @param {String} msg The validation message
33630 * Fires after the field has been validated with no errors.
33631 * @param {Roo.form.Field} this
33637 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33640 labelAlign : 'top',
33642 dayAllowBlank : false,
33643 monthAllowBlank : false,
33644 yearAllowBlank : false,
33645 dayPlaceholder : '',
33646 monthPlaceholder : '',
33647 yearPlaceholder : '',
33651 isFormField : true,
33657 getAutoCreate : function()
33661 cls : 'row roo-date-split-field-group',
33666 cls : 'form-hidden-field roo-date-split-field-group-value',
33672 var labelCls = 'col-md-12';
33673 var contentCls = 'col-md-4';
33675 if(this.fieldLabel){
33679 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33683 html : this.fieldLabel
33688 if(this.labelAlign == 'left'){
33690 if(this.labelWidth > 12){
33691 label.style = "width: " + this.labelWidth + 'px';
33694 if(this.labelWidth < 13 && this.labelmd == 0){
33695 this.labelmd = this.labelWidth;
33698 if(this.labellg > 0){
33699 labelCls = ' col-lg-' + this.labellg;
33700 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33703 if(this.labelmd > 0){
33704 labelCls = ' col-md-' + this.labelmd;
33705 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33708 if(this.labelsm > 0){
33709 labelCls = ' col-sm-' + this.labelsm;
33710 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33713 if(this.labelxs > 0){
33714 labelCls = ' col-xs-' + this.labelxs;
33715 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33719 label.cls += ' ' + labelCls;
33721 cfg.cn.push(label);
33724 Roo.each(['day', 'month', 'year'], function(t){
33727 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33734 inputEl: function ()
33736 return this.el.select('.roo-date-split-field-group-value', true).first();
33739 onRender : function(ct, position)
33743 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33745 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33747 this.dayField = new Roo.bootstrap.ComboBox({
33748 allowBlank : this.dayAllowBlank,
33749 alwaysQuery : true,
33750 displayField : 'value',
33753 forceSelection : true,
33755 placeholder : this.dayPlaceholder,
33756 selectOnFocus : true,
33757 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33758 triggerAction : 'all',
33760 valueField : 'value',
33761 store : new Roo.data.SimpleStore({
33762 data : (function() {
33764 _this.fireEvent('days', _this, days);
33767 fields : [ 'value' ]
33770 select : function (_self, record, index)
33772 _this.setValue(_this.getValue());
33777 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33779 this.monthField = new Roo.bootstrap.MonthField({
33780 after : '<i class=\"fa fa-calendar\"></i>',
33781 allowBlank : this.monthAllowBlank,
33782 placeholder : this.monthPlaceholder,
33785 render : function (_self)
33787 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33788 e.preventDefault();
33792 select : function (_self, oldvalue, newvalue)
33794 _this.setValue(_this.getValue());
33799 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33801 this.yearField = new Roo.bootstrap.ComboBox({
33802 allowBlank : this.yearAllowBlank,
33803 alwaysQuery : true,
33804 displayField : 'value',
33807 forceSelection : true,
33809 placeholder : this.yearPlaceholder,
33810 selectOnFocus : true,
33811 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33812 triggerAction : 'all',
33814 valueField : 'value',
33815 store : new Roo.data.SimpleStore({
33816 data : (function() {
33818 _this.fireEvent('years', _this, years);
33821 fields : [ 'value' ]
33824 select : function (_self, record, index)
33826 _this.setValue(_this.getValue());
33831 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33834 setValue : function(v, format)
33836 this.inputEl.dom.value = v;
33838 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33840 var d = Date.parseDate(v, f);
33847 this.setDay(d.format(this.dayFormat));
33848 this.setMonth(d.format(this.monthFormat));
33849 this.setYear(d.format(this.yearFormat));
33856 setDay : function(v)
33858 this.dayField.setValue(v);
33859 this.inputEl.dom.value = this.getValue();
33864 setMonth : function(v)
33866 this.monthField.setValue(v, true);
33867 this.inputEl.dom.value = this.getValue();
33872 setYear : function(v)
33874 this.yearField.setValue(v);
33875 this.inputEl.dom.value = this.getValue();
33880 getDay : function()
33882 return this.dayField.getValue();
33885 getMonth : function()
33887 return this.monthField.getValue();
33890 getYear : function()
33892 return this.yearField.getValue();
33895 getValue : function()
33897 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33899 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33909 this.inputEl.dom.value = '';
33914 validate : function()
33916 var d = this.dayField.validate();
33917 var m = this.monthField.validate();
33918 var y = this.yearField.validate();
33923 (!this.dayAllowBlank && !d) ||
33924 (!this.monthAllowBlank && !m) ||
33925 (!this.yearAllowBlank && !y)
33930 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33939 this.markInvalid();
33944 markValid : function()
33947 var label = this.el.select('label', true).first();
33948 var icon = this.el.select('i.fa-star', true).first();
33954 this.fireEvent('valid', this);
33958 * Mark this field as invalid
33959 * @param {String} msg The validation message
33961 markInvalid : function(msg)
33964 var label = this.el.select('label', true).first();
33965 var icon = this.el.select('i.fa-star', true).first();
33967 if(label && !icon){
33968 this.el.select('.roo-date-split-field-label', true).createChild({
33970 cls : 'text-danger fa fa-lg fa-star',
33971 tooltip : 'This field is required',
33972 style : 'margin-right:5px;'
33976 this.fireEvent('invalid', this, msg);
33979 clearInvalid : function()
33981 var label = this.el.select('label', true).first();
33982 var icon = this.el.select('i.fa-star', true).first();
33988 this.fireEvent('valid', this);
33991 getName: function()
34001 * @class Roo.bootstrap.LayoutMasonry
34002 * @extends Roo.bootstrap.Component
34003 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34004 * Bootstrap Layout Masonry class
34007 * http://masonry.desandro.com
34009 * The idea is to render all the bricks based on vertical width...
34011 * The original code extends 'outlayer' - we might need to use that....
34014 * Create a new Element
34015 * @param {Object} config The config object
34018 Roo.bootstrap.LayoutMasonry = function(config){
34020 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34024 Roo.bootstrap.LayoutMasonry.register(this);
34030 * Fire after layout the items
34031 * @param {Roo.bootstrap.LayoutMasonry} this
34032 * @param {Roo.EventObject} e
34039 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34042 * @cfg {Boolean} isLayoutInstant = no animation?
34044 isLayoutInstant : false, // needed?
34047 * @cfg {Number} boxWidth width of the columns
34052 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34057 * @cfg {Number} padWidth padding below box..
34062 * @cfg {Number} gutter gutter width..
34067 * @cfg {Number} maxCols maximum number of columns
34073 * @cfg {Boolean} isAutoInitial defalut true
34075 isAutoInitial : true,
34080 * @cfg {Boolean} isHorizontal defalut false
34082 isHorizontal : false,
34084 currentSize : null,
34090 bricks: null, //CompositeElement
34094 _isLayoutInited : false,
34096 // isAlternative : false, // only use for vertical layout...
34099 * @cfg {Number} alternativePadWidth padding below box..
34101 alternativePadWidth : 50,
34103 selectedBrick : [],
34105 getAutoCreate : function(){
34107 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34111 cls: 'blog-masonary-wrapper ' + this.cls,
34113 cls : 'mas-boxes masonary'
34120 getChildContainer: function( )
34122 if (this.boxesEl) {
34123 return this.boxesEl;
34126 this.boxesEl = this.el.select('.mas-boxes').first();
34128 return this.boxesEl;
34132 initEvents : function()
34136 if(this.isAutoInitial){
34137 Roo.log('hook children rendered');
34138 this.on('childrenrendered', function() {
34139 Roo.log('children rendered');
34145 initial : function()
34147 this.selectedBrick = [];
34149 this.currentSize = this.el.getBox(true);
34151 Roo.EventManager.onWindowResize(this.resize, this);
34153 if(!this.isAutoInitial){
34161 //this.layout.defer(500,this);
34165 resize : function()
34167 var cs = this.el.getBox(true);
34170 this.currentSize.width == cs.width &&
34171 this.currentSize.x == cs.x &&
34172 this.currentSize.height == cs.height &&
34173 this.currentSize.y == cs.y
34175 Roo.log("no change in with or X or Y");
34179 this.currentSize = cs;
34185 layout : function()
34187 this._resetLayout();
34189 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34191 this.layoutItems( isInstant );
34193 this._isLayoutInited = true;
34195 this.fireEvent('layout', this);
34199 _resetLayout : function()
34201 if(this.isHorizontal){
34202 this.horizontalMeasureColumns();
34206 this.verticalMeasureColumns();
34210 verticalMeasureColumns : function()
34212 this.getContainerWidth();
34214 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34215 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34219 var boxWidth = this.boxWidth + this.padWidth;
34221 if(this.containerWidth < this.boxWidth){
34222 boxWidth = this.containerWidth
34225 var containerWidth = this.containerWidth;
34227 var cols = Math.floor(containerWidth / boxWidth);
34229 this.cols = Math.max( cols, 1 );
34231 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34233 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34235 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34237 this.colWidth = boxWidth + avail - this.padWidth;
34239 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34240 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34243 horizontalMeasureColumns : function()
34245 this.getContainerWidth();
34247 var boxWidth = this.boxWidth;
34249 if(this.containerWidth < boxWidth){
34250 boxWidth = this.containerWidth;
34253 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34255 this.el.setHeight(boxWidth);
34259 getContainerWidth : function()
34261 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34264 layoutItems : function( isInstant )
34266 Roo.log(this.bricks);
34268 var items = Roo.apply([], this.bricks);
34270 if(this.isHorizontal){
34271 this._horizontalLayoutItems( items , isInstant );
34275 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34276 // this._verticalAlternativeLayoutItems( items , isInstant );
34280 this._verticalLayoutItems( items , isInstant );
34284 _verticalLayoutItems : function ( items , isInstant)
34286 if ( !items || !items.length ) {
34291 ['xs', 'xs', 'xs', 'tall'],
34292 ['xs', 'xs', 'tall'],
34293 ['xs', 'xs', 'sm'],
34294 ['xs', 'xs', 'xs'],
34300 ['sm', 'xs', 'xs'],
34304 ['tall', 'xs', 'xs', 'xs'],
34305 ['tall', 'xs', 'xs'],
34317 Roo.each(items, function(item, k){
34319 switch (item.size) {
34320 // these layouts take up a full box,
34331 boxes.push([item]);
34354 var filterPattern = function(box, length)
34362 var pattern = box.slice(0, length);
34366 Roo.each(pattern, function(i){
34367 format.push(i.size);
34370 Roo.each(standard, function(s){
34372 if(String(s) != String(format)){
34381 if(!match && length == 1){
34386 filterPattern(box, length - 1);
34390 queue.push(pattern);
34392 box = box.slice(length, box.length);
34394 filterPattern(box, 4);
34400 Roo.each(boxes, function(box, k){
34406 if(box.length == 1){
34411 filterPattern(box, 4);
34415 this._processVerticalLayoutQueue( queue, isInstant );
34419 // _verticalAlternativeLayoutItems : function( items , isInstant )
34421 // if ( !items || !items.length ) {
34425 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34429 _horizontalLayoutItems : function ( items , isInstant)
34431 if ( !items || !items.length || items.length < 3) {
34437 var eItems = items.slice(0, 3);
34439 items = items.slice(3, items.length);
34442 ['xs', 'xs', 'xs', 'wide'],
34443 ['xs', 'xs', 'wide'],
34444 ['xs', 'xs', 'sm'],
34445 ['xs', 'xs', 'xs'],
34451 ['sm', 'xs', 'xs'],
34455 ['wide', 'xs', 'xs', 'xs'],
34456 ['wide', 'xs', 'xs'],
34469 Roo.each(items, function(item, k){
34471 switch (item.size) {
34482 boxes.push([item]);
34506 var filterPattern = function(box, length)
34514 var pattern = box.slice(0, length);
34518 Roo.each(pattern, function(i){
34519 format.push(i.size);
34522 Roo.each(standard, function(s){
34524 if(String(s) != String(format)){
34533 if(!match && length == 1){
34538 filterPattern(box, length - 1);
34542 queue.push(pattern);
34544 box = box.slice(length, box.length);
34546 filterPattern(box, 4);
34552 Roo.each(boxes, function(box, k){
34558 if(box.length == 1){
34563 filterPattern(box, 4);
34570 var pos = this.el.getBox(true);
34574 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34576 var hit_end = false;
34578 Roo.each(queue, function(box){
34582 Roo.each(box, function(b){
34584 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34594 Roo.each(box, function(b){
34596 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34599 mx = Math.max(mx, b.x);
34603 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34607 Roo.each(box, function(b){
34609 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34623 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34626 /** Sets position of item in DOM
34627 * @param {Element} item
34628 * @param {Number} x - horizontal position
34629 * @param {Number} y - vertical position
34630 * @param {Boolean} isInstant - disables transitions
34632 _processVerticalLayoutQueue : function( queue, isInstant )
34634 var pos = this.el.getBox(true);
34639 for (var i = 0; i < this.cols; i++){
34643 Roo.each(queue, function(box, k){
34645 var col = k % this.cols;
34647 Roo.each(box, function(b,kk){
34649 b.el.position('absolute');
34651 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34652 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34654 if(b.size == 'md-left' || b.size == 'md-right'){
34655 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34656 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34659 b.el.setWidth(width);
34660 b.el.setHeight(height);
34662 b.el.select('iframe',true).setSize(width,height);
34666 for (var i = 0; i < this.cols; i++){
34668 if(maxY[i] < maxY[col]){
34673 col = Math.min(col, i);
34677 x = pos.x + col * (this.colWidth + this.padWidth);
34681 var positions = [];
34683 switch (box.length){
34685 positions = this.getVerticalOneBoxColPositions(x, y, box);
34688 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34691 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34694 positions = this.getVerticalFourBoxColPositions(x, y, box);
34700 Roo.each(box, function(b,kk){
34702 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34704 var sz = b.el.getSize();
34706 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34714 for (var i = 0; i < this.cols; i++){
34715 mY = Math.max(mY, maxY[i]);
34718 this.el.setHeight(mY - pos.y);
34722 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34724 // var pos = this.el.getBox(true);
34727 // var maxX = pos.right;
34729 // var maxHeight = 0;
34731 // Roo.each(items, function(item, k){
34735 // item.el.position('absolute');
34737 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34739 // item.el.setWidth(width);
34741 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34743 // item.el.setHeight(height);
34746 // item.el.setXY([x, y], isInstant ? false : true);
34748 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34751 // y = y + height + this.alternativePadWidth;
34753 // maxHeight = maxHeight + height + this.alternativePadWidth;
34757 // this.el.setHeight(maxHeight);
34761 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34763 var pos = this.el.getBox(true);
34768 var maxX = pos.right;
34770 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34772 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34774 Roo.each(queue, function(box, k){
34776 Roo.each(box, function(b, kk){
34778 b.el.position('absolute');
34780 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34781 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34783 if(b.size == 'md-left' || b.size == 'md-right'){
34784 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34785 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34788 b.el.setWidth(width);
34789 b.el.setHeight(height);
34797 var positions = [];
34799 switch (box.length){
34801 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34804 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34807 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34810 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34816 Roo.each(box, function(b,kk){
34818 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34820 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34828 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34830 Roo.each(eItems, function(b,k){
34832 b.size = (k == 0) ? 'sm' : 'xs';
34833 b.x = (k == 0) ? 2 : 1;
34834 b.y = (k == 0) ? 2 : 1;
34836 b.el.position('absolute');
34838 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34840 b.el.setWidth(width);
34842 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34844 b.el.setHeight(height);
34848 var positions = [];
34851 x : maxX - this.unitWidth * 2 - this.gutter,
34856 x : maxX - this.unitWidth,
34857 y : minY + (this.unitWidth + this.gutter) * 2
34861 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34865 Roo.each(eItems, function(b,k){
34867 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34873 getVerticalOneBoxColPositions : function(x, y, box)
34877 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34879 if(box[0].size == 'md-left'){
34883 if(box[0].size == 'md-right'){
34888 x : x + (this.unitWidth + this.gutter) * rand,
34895 getVerticalTwoBoxColPositions : function(x, y, box)
34899 if(box[0].size == 'xs'){
34903 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34907 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34921 x : x + (this.unitWidth + this.gutter) * 2,
34922 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34929 getVerticalThreeBoxColPositions : function(x, y, box)
34933 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34941 x : x + (this.unitWidth + this.gutter) * 1,
34946 x : x + (this.unitWidth + this.gutter) * 2,
34954 if(box[0].size == 'xs' && box[1].size == 'xs'){
34963 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34967 x : x + (this.unitWidth + this.gutter) * 1,
34981 x : x + (this.unitWidth + this.gutter) * 2,
34986 x : x + (this.unitWidth + this.gutter) * 2,
34987 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34994 getVerticalFourBoxColPositions : function(x, y, box)
34998 if(box[0].size == 'xs'){
35007 y : y + (this.unitHeight + this.gutter) * 1
35012 y : y + (this.unitHeight + this.gutter) * 2
35016 x : x + (this.unitWidth + this.gutter) * 1,
35030 x : x + (this.unitWidth + this.gutter) * 2,
35035 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35036 y : y + (this.unitHeight + this.gutter) * 1
35040 x : x + (this.unitWidth + this.gutter) * 2,
35041 y : y + (this.unitWidth + this.gutter) * 2
35048 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35052 if(box[0].size == 'md-left'){
35054 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35061 if(box[0].size == 'md-right'){
35063 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35064 y : minY + (this.unitWidth + this.gutter) * 1
35070 var rand = Math.floor(Math.random() * (4 - box[0].y));
35073 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35074 y : minY + (this.unitWidth + this.gutter) * rand
35081 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35085 if(box[0].size == 'xs'){
35088 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35093 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35094 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35102 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35107 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35108 y : minY + (this.unitWidth + this.gutter) * 2
35115 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35119 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35122 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35127 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35128 y : minY + (this.unitWidth + this.gutter) * 1
35132 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35133 y : minY + (this.unitWidth + this.gutter) * 2
35140 if(box[0].size == 'xs' && box[1].size == 'xs'){
35143 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35148 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35153 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35154 y : minY + (this.unitWidth + this.gutter) * 1
35162 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35167 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35168 y : minY + (this.unitWidth + this.gutter) * 2
35172 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35173 y : minY + (this.unitWidth + this.gutter) * 2
35180 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35184 if(box[0].size == 'xs'){
35187 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35192 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35197 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),
35202 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35203 y : minY + (this.unitWidth + this.gutter) * 1
35211 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35216 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35217 y : minY + (this.unitWidth + this.gutter) * 2
35221 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35222 y : minY + (this.unitWidth + this.gutter) * 2
35226 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),
35227 y : minY + (this.unitWidth + this.gutter) * 2
35235 * remove a Masonry Brick
35236 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35238 removeBrick : function(brick_id)
35244 for (var i = 0; i<this.bricks.length; i++) {
35245 if (this.bricks[i].id == brick_id) {
35246 this.bricks.splice(i,1);
35247 this.el.dom.removeChild(Roo.get(brick_id).dom);
35254 * adds a Masonry Brick
35255 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35257 addBrick : function(cfg)
35259 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35260 //this.register(cn);
35261 cn.parentId = this.id;
35262 cn.render(this.el);
35267 * register a Masonry Brick
35268 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35271 register : function(brick)
35273 this.bricks.push(brick);
35274 brick.masonryId = this.id;
35278 * clear all the Masonry Brick
35280 clearAll : function()
35283 //this.getChildContainer().dom.innerHTML = "";
35284 this.el.dom.innerHTML = '';
35287 getSelected : function()
35289 if (!this.selectedBrick) {
35293 return this.selectedBrick;
35297 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35301 * register a Masonry Layout
35302 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35305 register : function(layout)
35307 this.groups[layout.id] = layout;
35310 * fetch a Masonry Layout based on the masonry layout ID
35311 * @param {string} the masonry layout to add
35312 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35315 get: function(layout_id) {
35316 if (typeof(this.groups[layout_id]) == 'undefined') {
35319 return this.groups[layout_id] ;
35331 * http://masonry.desandro.com
35333 * The idea is to render all the bricks based on vertical width...
35335 * The original code extends 'outlayer' - we might need to use that....
35341 * @class Roo.bootstrap.LayoutMasonryAuto
35342 * @extends Roo.bootstrap.Component
35343 * Bootstrap Layout Masonry class
35346 * Create a new Element
35347 * @param {Object} config The config object
35350 Roo.bootstrap.LayoutMasonryAuto = function(config){
35351 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35354 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35357 * @cfg {Boolean} isFitWidth - resize the width..
35359 isFitWidth : false, // options..
35361 * @cfg {Boolean} isOriginLeft = left align?
35363 isOriginLeft : true,
35365 * @cfg {Boolean} isOriginTop = top align?
35367 isOriginTop : false,
35369 * @cfg {Boolean} isLayoutInstant = no animation?
35371 isLayoutInstant : false, // needed?
35373 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35375 isResizingContainer : true,
35377 * @cfg {Number} columnWidth width of the columns
35383 * @cfg {Number} maxCols maximum number of columns
35388 * @cfg {Number} padHeight padding below box..
35394 * @cfg {Boolean} isAutoInitial defalut true
35397 isAutoInitial : true,
35403 initialColumnWidth : 0,
35404 currentSize : null,
35406 colYs : null, // array.
35413 bricks: null, //CompositeElement
35414 cols : 0, // array?
35415 // element : null, // wrapped now this.el
35416 _isLayoutInited : null,
35419 getAutoCreate : function(){
35423 cls: 'blog-masonary-wrapper ' + this.cls,
35425 cls : 'mas-boxes masonary'
35432 getChildContainer: function( )
35434 if (this.boxesEl) {
35435 return this.boxesEl;
35438 this.boxesEl = this.el.select('.mas-boxes').first();
35440 return this.boxesEl;
35444 initEvents : function()
35448 if(this.isAutoInitial){
35449 Roo.log('hook children rendered');
35450 this.on('childrenrendered', function() {
35451 Roo.log('children rendered');
35458 initial : function()
35460 this.reloadItems();
35462 this.currentSize = this.el.getBox(true);
35464 /// was window resize... - let's see if this works..
35465 Roo.EventManager.onWindowResize(this.resize, this);
35467 if(!this.isAutoInitial){
35472 this.layout.defer(500,this);
35475 reloadItems: function()
35477 this.bricks = this.el.select('.masonry-brick', true);
35479 this.bricks.each(function(b) {
35480 //Roo.log(b.getSize());
35481 if (!b.attr('originalwidth')) {
35482 b.attr('originalwidth', b.getSize().width);
35487 Roo.log(this.bricks.elements.length);
35490 resize : function()
35493 var cs = this.el.getBox(true);
35495 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35496 Roo.log("no change in with or X");
35499 this.currentSize = cs;
35503 layout : function()
35506 this._resetLayout();
35507 //this._manageStamps();
35509 // don't animate first layout
35510 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35511 this.layoutItems( isInstant );
35513 // flag for initalized
35514 this._isLayoutInited = true;
35517 layoutItems : function( isInstant )
35519 //var items = this._getItemsForLayout( this.items );
35520 // original code supports filtering layout items.. we just ignore it..
35522 this._layoutItems( this.bricks , isInstant );
35524 this._postLayout();
35526 _layoutItems : function ( items , isInstant)
35528 //this.fireEvent( 'layout', this, items );
35531 if ( !items || !items.elements.length ) {
35532 // no items, emit event with empty array
35537 items.each(function(item) {
35538 Roo.log("layout item");
35540 // get x/y object from method
35541 var position = this._getItemLayoutPosition( item );
35543 position.item = item;
35544 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35545 queue.push( position );
35548 this._processLayoutQueue( queue );
35550 /** Sets position of item in DOM
35551 * @param {Element} item
35552 * @param {Number} x - horizontal position
35553 * @param {Number} y - vertical position
35554 * @param {Boolean} isInstant - disables transitions
35556 _processLayoutQueue : function( queue )
35558 for ( var i=0, len = queue.length; i < len; i++ ) {
35559 var obj = queue[i];
35560 obj.item.position('absolute');
35561 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35567 * Any logic you want to do after each layout,
35568 * i.e. size the container
35570 _postLayout : function()
35572 this.resizeContainer();
35575 resizeContainer : function()
35577 if ( !this.isResizingContainer ) {
35580 var size = this._getContainerSize();
35582 this.el.setSize(size.width,size.height);
35583 this.boxesEl.setSize(size.width,size.height);
35589 _resetLayout : function()
35591 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35592 this.colWidth = this.el.getWidth();
35593 //this.gutter = this.el.getWidth();
35595 this.measureColumns();
35601 this.colYs.push( 0 );
35607 measureColumns : function()
35609 this.getContainerWidth();
35610 // if columnWidth is 0, default to outerWidth of first item
35611 if ( !this.columnWidth ) {
35612 var firstItem = this.bricks.first();
35613 Roo.log(firstItem);
35614 this.columnWidth = this.containerWidth;
35615 if (firstItem && firstItem.attr('originalwidth') ) {
35616 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35618 // columnWidth fall back to item of first element
35619 Roo.log("set column width?");
35620 this.initialColumnWidth = this.columnWidth ;
35622 // if first elem has no width, default to size of container
35627 if (this.initialColumnWidth) {
35628 this.columnWidth = this.initialColumnWidth;
35633 // column width is fixed at the top - however if container width get's smaller we should
35636 // this bit calcs how man columns..
35638 var columnWidth = this.columnWidth += this.gutter;
35640 // calculate columns
35641 var containerWidth = this.containerWidth + this.gutter;
35643 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35644 // fix rounding errors, typically with gutters
35645 var excess = columnWidth - containerWidth % columnWidth;
35648 // if overshoot is less than a pixel, round up, otherwise floor it
35649 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35650 cols = Math[ mathMethod ]( cols );
35651 this.cols = Math.max( cols, 1 );
35652 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35654 // padding positioning..
35655 var totalColWidth = this.cols * this.columnWidth;
35656 var padavail = this.containerWidth - totalColWidth;
35657 // so for 2 columns - we need 3 'pads'
35659 var padNeeded = (1+this.cols) * this.padWidth;
35661 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35663 this.columnWidth += padExtra
35664 //this.padWidth = Math.floor(padavail / ( this.cols));
35666 // adjust colum width so that padding is fixed??
35668 // we have 3 columns ... total = width * 3
35669 // we have X left over... that should be used by
35671 //if (this.expandC) {
35679 getContainerWidth : function()
35681 /* // container is parent if fit width
35682 var container = this.isFitWidth ? this.element.parentNode : this.element;
35683 // check that this.size and size are there
35684 // IE8 triggers resize on body size change, so they might not be
35686 var size = getSize( container ); //FIXME
35687 this.containerWidth = size && size.innerWidth; //FIXME
35690 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35694 _getItemLayoutPosition : function( item ) // what is item?
35696 // we resize the item to our columnWidth..
35698 item.setWidth(this.columnWidth);
35699 item.autoBoxAdjust = false;
35701 var sz = item.getSize();
35703 // how many columns does this brick span
35704 var remainder = this.containerWidth % this.columnWidth;
35706 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35707 // round if off by 1 pixel, otherwise use ceil
35708 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35709 colSpan = Math.min( colSpan, this.cols );
35711 // normally this should be '1' as we dont' currently allow multi width columns..
35713 var colGroup = this._getColGroup( colSpan );
35714 // get the minimum Y value from the columns
35715 var minimumY = Math.min.apply( Math, colGroup );
35716 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35718 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35720 // position the brick
35722 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35723 y: this.currentSize.y + minimumY + this.padHeight
35727 // apply setHeight to necessary columns
35728 var setHeight = minimumY + sz.height + this.padHeight;
35729 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35731 var setSpan = this.cols + 1 - colGroup.length;
35732 for ( var i = 0; i < setSpan; i++ ) {
35733 this.colYs[ shortColIndex + i ] = setHeight ;
35740 * @param {Number} colSpan - number of columns the element spans
35741 * @returns {Array} colGroup
35743 _getColGroup : function( colSpan )
35745 if ( colSpan < 2 ) {
35746 // if brick spans only one column, use all the column Ys
35751 // how many different places could this brick fit horizontally
35752 var groupCount = this.cols + 1 - colSpan;
35753 // for each group potential horizontal position
35754 for ( var i = 0; i < groupCount; i++ ) {
35755 // make an array of colY values for that one group
35756 var groupColYs = this.colYs.slice( i, i + colSpan );
35757 // and get the max value of the array
35758 colGroup[i] = Math.max.apply( Math, groupColYs );
35763 _manageStamp : function( stamp )
35765 var stampSize = stamp.getSize();
35766 var offset = stamp.getBox();
35767 // get the columns that this stamp affects
35768 var firstX = this.isOriginLeft ? offset.x : offset.right;
35769 var lastX = firstX + stampSize.width;
35770 var firstCol = Math.floor( firstX / this.columnWidth );
35771 firstCol = Math.max( 0, firstCol );
35773 var lastCol = Math.floor( lastX / this.columnWidth );
35774 // lastCol should not go over if multiple of columnWidth #425
35775 lastCol -= lastX % this.columnWidth ? 0 : 1;
35776 lastCol = Math.min( this.cols - 1, lastCol );
35778 // set colYs to bottom of the stamp
35779 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35782 for ( var i = firstCol; i <= lastCol; i++ ) {
35783 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35788 _getContainerSize : function()
35790 this.maxY = Math.max.apply( Math, this.colYs );
35795 if ( this.isFitWidth ) {
35796 size.width = this._getContainerFitWidth();
35802 _getContainerFitWidth : function()
35804 var unusedCols = 0;
35805 // count unused columns
35808 if ( this.colYs[i] !== 0 ) {
35813 // fit container to columns that have been used
35814 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35817 needsResizeLayout : function()
35819 var previousWidth = this.containerWidth;
35820 this.getContainerWidth();
35821 return previousWidth !== this.containerWidth;
35836 * @class Roo.bootstrap.MasonryBrick
35837 * @extends Roo.bootstrap.Component
35838 * Bootstrap MasonryBrick class
35841 * Create a new MasonryBrick
35842 * @param {Object} config The config object
35845 Roo.bootstrap.MasonryBrick = function(config){
35847 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35849 Roo.bootstrap.MasonryBrick.register(this);
35855 * When a MasonryBrick is clcik
35856 * @param {Roo.bootstrap.MasonryBrick} this
35857 * @param {Roo.EventObject} e
35863 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35866 * @cfg {String} title
35870 * @cfg {String} html
35874 * @cfg {String} bgimage
35878 * @cfg {String} videourl
35882 * @cfg {String} cls
35886 * @cfg {String} href
35890 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35895 * @cfg {String} placetitle (center|bottom)
35900 * @cfg {Boolean} isFitContainer defalut true
35902 isFitContainer : true,
35905 * @cfg {Boolean} preventDefault defalut false
35907 preventDefault : false,
35910 * @cfg {Boolean} inverse defalut false
35912 maskInverse : false,
35914 getAutoCreate : function()
35916 if(!this.isFitContainer){
35917 return this.getSplitAutoCreate();
35920 var cls = 'masonry-brick masonry-brick-full';
35922 if(this.href.length){
35923 cls += ' masonry-brick-link';
35926 if(this.bgimage.length){
35927 cls += ' masonry-brick-image';
35930 if(this.maskInverse){
35931 cls += ' mask-inverse';
35934 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35935 cls += ' enable-mask';
35939 cls += ' masonry-' + this.size + '-brick';
35942 if(this.placetitle.length){
35944 switch (this.placetitle) {
35946 cls += ' masonry-center-title';
35949 cls += ' masonry-bottom-title';
35956 if(!this.html.length && !this.bgimage.length){
35957 cls += ' masonry-center-title';
35960 if(!this.html.length && this.bgimage.length){
35961 cls += ' masonry-bottom-title';
35966 cls += ' ' + this.cls;
35970 tag: (this.href.length) ? 'a' : 'div',
35975 cls: 'masonry-brick-mask'
35979 cls: 'masonry-brick-paragraph',
35985 if(this.href.length){
35986 cfg.href = this.href;
35989 var cn = cfg.cn[1].cn;
35991 if(this.title.length){
35994 cls: 'masonry-brick-title',
35999 if(this.html.length){
36002 cls: 'masonry-brick-text',
36007 if (!this.title.length && !this.html.length) {
36008 cfg.cn[1].cls += ' hide';
36011 if(this.bgimage.length){
36014 cls: 'masonry-brick-image-view',
36019 if(this.videourl.length){
36020 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36021 // youtube support only?
36024 cls: 'masonry-brick-image-view',
36027 allowfullscreen : true
36035 getSplitAutoCreate : function()
36037 var cls = 'masonry-brick masonry-brick-split';
36039 if(this.href.length){
36040 cls += ' masonry-brick-link';
36043 if(this.bgimage.length){
36044 cls += ' masonry-brick-image';
36048 cls += ' masonry-' + this.size + '-brick';
36051 switch (this.placetitle) {
36053 cls += ' masonry-center-title';
36056 cls += ' masonry-bottom-title';
36059 if(!this.bgimage.length){
36060 cls += ' masonry-center-title';
36063 if(this.bgimage.length){
36064 cls += ' masonry-bottom-title';
36070 cls += ' ' + this.cls;
36074 tag: (this.href.length) ? 'a' : 'div',
36079 cls: 'masonry-brick-split-head',
36083 cls: 'masonry-brick-paragraph',
36090 cls: 'masonry-brick-split-body',
36096 if(this.href.length){
36097 cfg.href = this.href;
36100 if(this.title.length){
36101 cfg.cn[0].cn[0].cn.push({
36103 cls: 'masonry-brick-title',
36108 if(this.html.length){
36109 cfg.cn[1].cn.push({
36111 cls: 'masonry-brick-text',
36116 if(this.bgimage.length){
36117 cfg.cn[0].cn.push({
36119 cls: 'masonry-brick-image-view',
36124 if(this.videourl.length){
36125 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36126 // youtube support only?
36127 cfg.cn[0].cn.cn.push({
36129 cls: 'masonry-brick-image-view',
36132 allowfullscreen : true
36139 initEvents: function()
36141 switch (this.size) {
36174 this.el.on('touchstart', this.onTouchStart, this);
36175 this.el.on('touchmove', this.onTouchMove, this);
36176 this.el.on('touchend', this.onTouchEnd, this);
36177 this.el.on('contextmenu', this.onContextMenu, this);
36179 this.el.on('mouseenter' ,this.enter, this);
36180 this.el.on('mouseleave', this.leave, this);
36181 this.el.on('click', this.onClick, this);
36184 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36185 this.parent().bricks.push(this);
36190 onClick: function(e, el)
36192 var time = this.endTimer - this.startTimer;
36193 // Roo.log(e.preventDefault());
36196 e.preventDefault();
36201 if(!this.preventDefault){
36205 e.preventDefault();
36207 if (this.activeClass != '') {
36208 this.selectBrick();
36211 this.fireEvent('click', this, e);
36214 enter: function(e, el)
36216 e.preventDefault();
36218 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36222 if(this.bgimage.length && this.html.length){
36223 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36227 leave: function(e, el)
36229 e.preventDefault();
36231 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36235 if(this.bgimage.length && this.html.length){
36236 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36240 onTouchStart: function(e, el)
36242 // e.preventDefault();
36244 this.touchmoved = false;
36246 if(!this.isFitContainer){
36250 if(!this.bgimage.length || !this.html.length){
36254 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36256 this.timer = new Date().getTime();
36260 onTouchMove: function(e, el)
36262 this.touchmoved = true;
36265 onContextMenu : function(e,el)
36267 e.preventDefault();
36268 e.stopPropagation();
36272 onTouchEnd: function(e, el)
36274 // e.preventDefault();
36276 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36283 if(!this.bgimage.length || !this.html.length){
36285 if(this.href.length){
36286 window.location.href = this.href;
36292 if(!this.isFitContainer){
36296 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36298 window.location.href = this.href;
36301 //selection on single brick only
36302 selectBrick : function() {
36304 if (!this.parentId) {
36308 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36309 var index = m.selectedBrick.indexOf(this.id);
36312 m.selectedBrick.splice(index,1);
36313 this.el.removeClass(this.activeClass);
36317 for(var i = 0; i < m.selectedBrick.length; i++) {
36318 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36319 b.el.removeClass(b.activeClass);
36322 m.selectedBrick = [];
36324 m.selectedBrick.push(this.id);
36325 this.el.addClass(this.activeClass);
36329 isSelected : function(){
36330 return this.el.hasClass(this.activeClass);
36335 Roo.apply(Roo.bootstrap.MasonryBrick, {
36338 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36340 * register a Masonry Brick
36341 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36344 register : function(brick)
36346 //this.groups[brick.id] = brick;
36347 this.groups.add(brick.id, brick);
36350 * fetch a masonry brick based on the masonry brick ID
36351 * @param {string} the masonry brick to add
36352 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36355 get: function(brick_id)
36357 // if (typeof(this.groups[brick_id]) == 'undefined') {
36360 // return this.groups[brick_id] ;
36362 if(this.groups.key(brick_id)) {
36363 return this.groups.key(brick_id);
36381 * @class Roo.bootstrap.Brick
36382 * @extends Roo.bootstrap.Component
36383 * Bootstrap Brick class
36386 * Create a new Brick
36387 * @param {Object} config The config object
36390 Roo.bootstrap.Brick = function(config){
36391 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36397 * When a Brick is click
36398 * @param {Roo.bootstrap.Brick} this
36399 * @param {Roo.EventObject} e
36405 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36408 * @cfg {String} title
36412 * @cfg {String} html
36416 * @cfg {String} bgimage
36420 * @cfg {String} cls
36424 * @cfg {String} href
36428 * @cfg {String} video
36432 * @cfg {Boolean} square
36436 getAutoCreate : function()
36438 var cls = 'roo-brick';
36440 if(this.href.length){
36441 cls += ' roo-brick-link';
36444 if(this.bgimage.length){
36445 cls += ' roo-brick-image';
36448 if(!this.html.length && !this.bgimage.length){
36449 cls += ' roo-brick-center-title';
36452 if(!this.html.length && this.bgimage.length){
36453 cls += ' roo-brick-bottom-title';
36457 cls += ' ' + this.cls;
36461 tag: (this.href.length) ? 'a' : 'div',
36466 cls: 'roo-brick-paragraph',
36472 if(this.href.length){
36473 cfg.href = this.href;
36476 var cn = cfg.cn[0].cn;
36478 if(this.title.length){
36481 cls: 'roo-brick-title',
36486 if(this.html.length){
36489 cls: 'roo-brick-text',
36496 if(this.bgimage.length){
36499 cls: 'roo-brick-image-view',
36507 initEvents: function()
36509 if(this.title.length || this.html.length){
36510 this.el.on('mouseenter' ,this.enter, this);
36511 this.el.on('mouseleave', this.leave, this);
36514 Roo.EventManager.onWindowResize(this.resize, this);
36516 if(this.bgimage.length){
36517 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36518 this.imageEl.on('load', this.onImageLoad, this);
36525 onImageLoad : function()
36530 resize : function()
36532 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36534 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36536 if(this.bgimage.length){
36537 var image = this.el.select('.roo-brick-image-view', true).first();
36539 image.setWidth(paragraph.getWidth());
36542 image.setHeight(paragraph.getWidth());
36545 this.el.setHeight(image.getHeight());
36546 paragraph.setHeight(image.getHeight());
36552 enter: function(e, el)
36554 e.preventDefault();
36556 if(this.bgimage.length){
36557 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36558 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36562 leave: function(e, el)
36564 e.preventDefault();
36566 if(this.bgimage.length){
36567 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36568 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36583 * @class Roo.bootstrap.NumberField
36584 * @extends Roo.bootstrap.Input
36585 * Bootstrap NumberField class
36591 * Create a new NumberField
36592 * @param {Object} config The config object
36595 Roo.bootstrap.NumberField = function(config){
36596 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36599 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36602 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36604 allowDecimals : true,
36606 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36608 decimalSeparator : ".",
36610 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36612 decimalPrecision : 2,
36614 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36616 allowNegative : true,
36619 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36623 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36625 minValue : Number.NEGATIVE_INFINITY,
36627 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36629 maxValue : Number.MAX_VALUE,
36631 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36633 minText : "The minimum value for this field is {0}",
36635 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36637 maxText : "The maximum value for this field is {0}",
36639 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36640 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36642 nanText : "{0} is not a valid number",
36644 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36646 thousandsDelimiter : false,
36648 * @cfg {String} valueAlign alignment of value
36650 valueAlign : "left",
36652 getAutoCreate : function()
36654 var hiddenInput = {
36658 cls: 'hidden-number-input'
36662 hiddenInput.name = this.name;
36667 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36669 this.name = hiddenInput.name;
36671 if(cfg.cn.length > 0) {
36672 cfg.cn.push(hiddenInput);
36679 initEvents : function()
36681 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36683 var allowed = "0123456789";
36685 if(this.allowDecimals){
36686 allowed += this.decimalSeparator;
36689 if(this.allowNegative){
36693 if(this.thousandsDelimiter) {
36697 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36699 var keyPress = function(e){
36701 var k = e.getKey();
36703 var c = e.getCharCode();
36706 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36707 allowed.indexOf(String.fromCharCode(c)) === -1
36713 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36717 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36722 this.el.on("keypress", keyPress, this);
36725 validateValue : function(value)
36728 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36732 var num = this.parseValue(value);
36735 this.markInvalid(String.format(this.nanText, value));
36739 if(num < this.minValue){
36740 this.markInvalid(String.format(this.minText, this.minValue));
36744 if(num > this.maxValue){
36745 this.markInvalid(String.format(this.maxText, this.maxValue));
36752 getValue : function()
36754 var v = this.hiddenEl().getValue();
36756 return this.fixPrecision(this.parseValue(v));
36759 parseValue : function(value)
36761 if(this.thousandsDelimiter) {
36763 r = new RegExp(",", "g");
36764 value = value.replace(r, "");
36767 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36768 return isNaN(value) ? '' : value;
36771 fixPrecision : function(value)
36773 if(this.thousandsDelimiter) {
36775 r = new RegExp(",", "g");
36776 value = value.replace(r, "");
36779 var nan = isNaN(value);
36781 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36782 return nan ? '' : value;
36784 return parseFloat(value).toFixed(this.decimalPrecision);
36787 setValue : function(v)
36789 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36795 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36797 this.inputEl().dom.value = (v == '') ? '' :
36798 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36800 if(!this.allowZero && v === '0') {
36801 this.hiddenEl().dom.value = '';
36802 this.inputEl().dom.value = '';
36809 decimalPrecisionFcn : function(v)
36811 return Math.floor(v);
36814 beforeBlur : function()
36816 var v = this.parseValue(this.getRawValue());
36818 if(v || v === 0 || v === ''){
36823 hiddenEl : function()
36825 return this.el.select('input.hidden-number-input',true).first();
36837 * @class Roo.bootstrap.DocumentSlider
36838 * @extends Roo.bootstrap.Component
36839 * Bootstrap DocumentSlider class
36842 * Create a new DocumentViewer
36843 * @param {Object} config The config object
36846 Roo.bootstrap.DocumentSlider = function(config){
36847 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36854 * Fire after initEvent
36855 * @param {Roo.bootstrap.DocumentSlider} this
36860 * Fire after update
36861 * @param {Roo.bootstrap.DocumentSlider} this
36867 * @param {Roo.bootstrap.DocumentSlider} this
36873 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36879 getAutoCreate : function()
36883 cls : 'roo-document-slider',
36887 cls : 'roo-document-slider-header',
36891 cls : 'roo-document-slider-header-title'
36897 cls : 'roo-document-slider-body',
36901 cls : 'roo-document-slider-prev',
36905 cls : 'fa fa-chevron-left'
36911 cls : 'roo-document-slider-thumb',
36915 cls : 'roo-document-slider-image'
36921 cls : 'roo-document-slider-next',
36925 cls : 'fa fa-chevron-right'
36937 initEvents : function()
36939 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36940 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36942 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36943 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36945 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36946 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36948 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36949 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36951 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36952 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36954 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36955 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36957 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36958 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36960 this.thumbEl.on('click', this.onClick, this);
36962 this.prevIndicator.on('click', this.prev, this);
36964 this.nextIndicator.on('click', this.next, this);
36968 initial : function()
36970 if(this.files.length){
36971 this.indicator = 1;
36975 this.fireEvent('initial', this);
36978 update : function()
36980 this.imageEl.attr('src', this.files[this.indicator - 1]);
36982 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36984 this.prevIndicator.show();
36986 if(this.indicator == 1){
36987 this.prevIndicator.hide();
36990 this.nextIndicator.show();
36992 if(this.indicator == this.files.length){
36993 this.nextIndicator.hide();
36996 this.thumbEl.scrollTo('top');
36998 this.fireEvent('update', this);
37001 onClick : function(e)
37003 e.preventDefault();
37005 this.fireEvent('click', this);
37010 e.preventDefault();
37012 this.indicator = Math.max(1, this.indicator - 1);
37019 e.preventDefault();
37021 this.indicator = Math.min(this.files.length, this.indicator + 1);
37035 * @class Roo.bootstrap.RadioSet
37036 * @extends Roo.bootstrap.Input
37037 * @children Roo.bootstrap.Radio
37038 * Bootstrap RadioSet class
37039 * @cfg {String} indicatorpos (left|right) default left
37040 * @cfg {Boolean} inline (true|false) inline the element (default true)
37041 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37043 * Create a new RadioSet
37044 * @param {Object} config The config object
37047 Roo.bootstrap.RadioSet = function(config){
37049 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37053 Roo.bootstrap.RadioSet.register(this);
37058 * Fires when the element is checked or unchecked.
37059 * @param {Roo.bootstrap.RadioSet} this This radio
37060 * @param {Roo.bootstrap.Radio} item The checked item
37065 * Fires when the element is click.
37066 * @param {Roo.bootstrap.RadioSet} this This radio set
37067 * @param {Roo.bootstrap.Radio} item The checked item
37068 * @param {Roo.EventObject} e The event object
37075 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37083 indicatorpos : 'left',
37085 getAutoCreate : function()
37089 cls : 'roo-radio-set-label',
37093 html : this.fieldLabel
37097 if (Roo.bootstrap.version == 3) {
37100 if(this.indicatorpos == 'left'){
37103 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37104 tooltip : 'This field is required'
37109 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37110 tooltip : 'This field is required'
37116 cls : 'roo-radio-set-items'
37119 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37121 if (align === 'left' && this.fieldLabel.length) {
37124 cls : "roo-radio-set-right",
37130 if(this.labelWidth > 12){
37131 label.style = "width: " + this.labelWidth + 'px';
37134 if(this.labelWidth < 13 && this.labelmd == 0){
37135 this.labelmd = this.labelWidth;
37138 if(this.labellg > 0){
37139 label.cls += ' col-lg-' + this.labellg;
37140 items.cls += ' col-lg-' + (12 - this.labellg);
37143 if(this.labelmd > 0){
37144 label.cls += ' col-md-' + this.labelmd;
37145 items.cls += ' col-md-' + (12 - this.labelmd);
37148 if(this.labelsm > 0){
37149 label.cls += ' col-sm-' + this.labelsm;
37150 items.cls += ' col-sm-' + (12 - this.labelsm);
37153 if(this.labelxs > 0){
37154 label.cls += ' col-xs-' + this.labelxs;
37155 items.cls += ' col-xs-' + (12 - this.labelxs);
37161 cls : 'roo-radio-set',
37165 cls : 'roo-radio-set-input',
37168 value : this.value ? this.value : ''
37175 if(this.weight.length){
37176 cfg.cls += ' roo-radio-' + this.weight;
37180 cfg.cls += ' roo-radio-set-inline';
37184 ['xs','sm','md','lg'].map(function(size){
37185 if (settings[size]) {
37186 cfg.cls += ' col-' + size + '-' + settings[size];
37194 initEvents : function()
37196 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37197 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37199 if(!this.fieldLabel.length){
37200 this.labelEl.hide();
37203 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37204 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37206 this.indicator = this.indicatorEl();
37208 if(this.indicator){
37209 this.indicator.addClass('invisible');
37212 this.originalValue = this.getValue();
37216 inputEl: function ()
37218 return this.el.select('.roo-radio-set-input', true).first();
37221 getChildContainer : function()
37223 return this.itemsEl;
37226 register : function(item)
37228 this.radioes.push(item);
37232 validate : function()
37234 if(this.getVisibilityEl().hasClass('hidden')){
37240 Roo.each(this.radioes, function(i){
37249 if(this.allowBlank) {
37253 if(this.disabled || valid){
37258 this.markInvalid();
37263 markValid : function()
37265 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37266 this.indicatorEl().removeClass('visible');
37267 this.indicatorEl().addClass('invisible');
37271 if (Roo.bootstrap.version == 3) {
37272 this.el.removeClass([this.invalidClass, this.validClass]);
37273 this.el.addClass(this.validClass);
37275 this.el.removeClass(['is-invalid','is-valid']);
37276 this.el.addClass(['is-valid']);
37278 this.fireEvent('valid', this);
37281 markInvalid : function(msg)
37283 if(this.allowBlank || this.disabled){
37287 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37288 this.indicatorEl().removeClass('invisible');
37289 this.indicatorEl().addClass('visible');
37291 if (Roo.bootstrap.version == 3) {
37292 this.el.removeClass([this.invalidClass, this.validClass]);
37293 this.el.addClass(this.invalidClass);
37295 this.el.removeClass(['is-invalid','is-valid']);
37296 this.el.addClass(['is-invalid']);
37299 this.fireEvent('invalid', this, msg);
37303 setValue : function(v, suppressEvent)
37305 if(this.value === v){
37312 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37315 Roo.each(this.radioes, function(i){
37317 i.el.removeClass('checked');
37320 Roo.each(this.radioes, function(i){
37322 if(i.value === v || i.value.toString() === v.toString()){
37324 i.el.addClass('checked');
37326 if(suppressEvent !== true){
37327 this.fireEvent('check', this, i);
37338 clearInvalid : function(){
37340 if(!this.el || this.preventMark){
37344 this.el.removeClass([this.invalidClass]);
37346 this.fireEvent('valid', this);
37351 Roo.apply(Roo.bootstrap.RadioSet, {
37355 register : function(set)
37357 this.groups[set.name] = set;
37360 get: function(name)
37362 if (typeof(this.groups[name]) == 'undefined') {
37366 return this.groups[name] ;
37372 * Ext JS Library 1.1.1
37373 * Copyright(c) 2006-2007, Ext JS, LLC.
37375 * Originally Released Under LGPL - original licence link has changed is not relivant.
37378 * <script type="text/javascript">
37383 * @class Roo.bootstrap.SplitBar
37384 * @extends Roo.util.Observable
37385 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37389 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37390 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37391 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37392 split.minSize = 100;
37393 split.maxSize = 600;
37394 split.animate = true;
37395 split.on('moved', splitterMoved);
37398 * Create a new SplitBar
37399 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37400 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37401 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37402 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37403 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37404 position of the SplitBar).
37406 Roo.bootstrap.SplitBar = function(cfg){
37411 // dragElement : elm
37412 // resizingElement: el,
37414 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37415 // placement : Roo.bootstrap.SplitBar.LEFT ,
37416 // existingProxy ???
37419 this.el = Roo.get(cfg.dragElement, true);
37420 this.el.dom.unselectable = "on";
37422 this.resizingEl = Roo.get(cfg.resizingElement, true);
37426 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37427 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37430 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37433 * The minimum size of the resizing element. (Defaults to 0)
37439 * The maximum size of the resizing element. (Defaults to 2000)
37442 this.maxSize = 2000;
37445 * Whether to animate the transition to the new size
37448 this.animate = false;
37451 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37454 this.useShim = false;
37459 if(!cfg.existingProxy){
37461 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37463 this.proxy = Roo.get(cfg.existingProxy).dom;
37466 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37469 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37472 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37475 this.dragSpecs = {};
37478 * @private The adapter to use to positon and resize elements
37480 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37481 this.adapter.init(this);
37483 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37485 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37486 this.el.addClass("roo-splitbar-h");
37489 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37490 this.el.addClass("roo-splitbar-v");
37496 * Fires when the splitter is moved (alias for {@link #event-moved})
37497 * @param {Roo.bootstrap.SplitBar} this
37498 * @param {Number} newSize the new width or height
37503 * Fires when the splitter is moved
37504 * @param {Roo.bootstrap.SplitBar} this
37505 * @param {Number} newSize the new width or height
37509 * @event beforeresize
37510 * Fires before the splitter is dragged
37511 * @param {Roo.bootstrap.SplitBar} this
37513 "beforeresize" : true,
37515 "beforeapply" : true
37518 Roo.util.Observable.call(this);
37521 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37522 onStartProxyDrag : function(x, y){
37523 this.fireEvent("beforeresize", this);
37525 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37527 o.enableDisplayMode("block");
37528 // all splitbars share the same overlay
37529 Roo.bootstrap.SplitBar.prototype.overlay = o;
37531 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37532 this.overlay.show();
37533 Roo.get(this.proxy).setDisplayed("block");
37534 var size = this.adapter.getElementSize(this);
37535 this.activeMinSize = this.getMinimumSize();;
37536 this.activeMaxSize = this.getMaximumSize();;
37537 var c1 = size - this.activeMinSize;
37538 var c2 = Math.max(this.activeMaxSize - size, 0);
37539 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37540 this.dd.resetConstraints();
37541 this.dd.setXConstraint(
37542 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37543 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37545 this.dd.setYConstraint(0, 0);
37547 this.dd.resetConstraints();
37548 this.dd.setXConstraint(0, 0);
37549 this.dd.setYConstraint(
37550 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37551 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37554 this.dragSpecs.startSize = size;
37555 this.dragSpecs.startPoint = [x, y];
37556 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37560 * @private Called after the drag operation by the DDProxy
37562 onEndProxyDrag : function(e){
37563 Roo.get(this.proxy).setDisplayed(false);
37564 var endPoint = Roo.lib.Event.getXY(e);
37566 this.overlay.hide();
37569 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37570 newSize = this.dragSpecs.startSize +
37571 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37572 endPoint[0] - this.dragSpecs.startPoint[0] :
37573 this.dragSpecs.startPoint[0] - endPoint[0]
37576 newSize = this.dragSpecs.startSize +
37577 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37578 endPoint[1] - this.dragSpecs.startPoint[1] :
37579 this.dragSpecs.startPoint[1] - endPoint[1]
37582 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37583 if(newSize != this.dragSpecs.startSize){
37584 if(this.fireEvent('beforeapply', this, newSize) !== false){
37585 this.adapter.setElementSize(this, newSize);
37586 this.fireEvent("moved", this, newSize);
37587 this.fireEvent("resize", this, newSize);
37593 * Get the adapter this SplitBar uses
37594 * @return The adapter object
37596 getAdapter : function(){
37597 return this.adapter;
37601 * Set the adapter this SplitBar uses
37602 * @param {Object} adapter A SplitBar adapter object
37604 setAdapter : function(adapter){
37605 this.adapter = adapter;
37606 this.adapter.init(this);
37610 * Gets the minimum size for the resizing element
37611 * @return {Number} The minimum size
37613 getMinimumSize : function(){
37614 return this.minSize;
37618 * Sets the minimum size for the resizing element
37619 * @param {Number} minSize The minimum size
37621 setMinimumSize : function(minSize){
37622 this.minSize = minSize;
37626 * Gets the maximum size for the resizing element
37627 * @return {Number} The maximum size
37629 getMaximumSize : function(){
37630 return this.maxSize;
37634 * Sets the maximum size for the resizing element
37635 * @param {Number} maxSize The maximum size
37637 setMaximumSize : function(maxSize){
37638 this.maxSize = maxSize;
37642 * Sets the initialize size for the resizing element
37643 * @param {Number} size The initial size
37645 setCurrentSize : function(size){
37646 var oldAnimate = this.animate;
37647 this.animate = false;
37648 this.adapter.setElementSize(this, size);
37649 this.animate = oldAnimate;
37653 * Destroy this splitbar.
37654 * @param {Boolean} removeEl True to remove the element
37656 destroy : function(removeEl){
37658 this.shim.remove();
37661 this.proxy.parentNode.removeChild(this.proxy);
37669 * @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.
37671 Roo.bootstrap.SplitBar.createProxy = function(dir){
37672 var proxy = new Roo.Element(document.createElement("div"));
37673 proxy.unselectable();
37674 var cls = 'roo-splitbar-proxy';
37675 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37676 document.body.appendChild(proxy.dom);
37681 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37682 * Default Adapter. It assumes the splitter and resizing element are not positioned
37683 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37685 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37688 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37689 // do nothing for now
37690 init : function(s){
37694 * Called before drag operations to get the current size of the resizing element.
37695 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37697 getElementSize : function(s){
37698 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37699 return s.resizingEl.getWidth();
37701 return s.resizingEl.getHeight();
37706 * Called after drag operations to set the size of the resizing element.
37707 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37708 * @param {Number} newSize The new size to set
37709 * @param {Function} onComplete A function to be invoked when resizing is complete
37711 setElementSize : function(s, newSize, onComplete){
37712 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37714 s.resizingEl.setWidth(newSize);
37716 onComplete(s, newSize);
37719 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37724 s.resizingEl.setHeight(newSize);
37726 onComplete(s, newSize);
37729 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37736 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37737 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37738 * Adapter that moves the splitter element to align with the resized sizing element.
37739 * Used with an absolute positioned SplitBar.
37740 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37741 * document.body, make sure you assign an id to the body element.
37743 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37744 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37745 this.container = Roo.get(container);
37748 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37749 init : function(s){
37750 this.basic.init(s);
37753 getElementSize : function(s){
37754 return this.basic.getElementSize(s);
37757 setElementSize : function(s, newSize, onComplete){
37758 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37761 moveSplitter : function(s){
37762 var yes = Roo.bootstrap.SplitBar;
37763 switch(s.placement){
37765 s.el.setX(s.resizingEl.getRight());
37768 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37771 s.el.setY(s.resizingEl.getBottom());
37774 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37781 * Orientation constant - Create a vertical SplitBar
37785 Roo.bootstrap.SplitBar.VERTICAL = 1;
37788 * Orientation constant - Create a horizontal SplitBar
37792 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37795 * Placement constant - The resizing element is to the left of the splitter element
37799 Roo.bootstrap.SplitBar.LEFT = 1;
37802 * Placement constant - The resizing element is to the right of the splitter element
37806 Roo.bootstrap.SplitBar.RIGHT = 2;
37809 * Placement constant - The resizing element is positioned above the splitter element
37813 Roo.bootstrap.SplitBar.TOP = 3;
37816 * Placement constant - The resizing element is positioned under splitter element
37820 Roo.bootstrap.SplitBar.BOTTOM = 4;
37821 Roo.namespace("Roo.bootstrap.layout");/*
37823 * Ext JS Library 1.1.1
37824 * Copyright(c) 2006-2007, Ext JS, LLC.
37826 * Originally Released Under LGPL - original licence link has changed is not relivant.
37829 * <script type="text/javascript">
37833 * @class Roo.bootstrap.layout.Manager
37834 * @extends Roo.bootstrap.Component
37835 * Base class for layout managers.
37837 Roo.bootstrap.layout.Manager = function(config)
37839 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37845 /** false to disable window resize monitoring @type Boolean */
37846 this.monitorWindowResize = true;
37851 * Fires when a layout is performed.
37852 * @param {Roo.LayoutManager} this
37856 * @event regionresized
37857 * Fires when the user resizes a region.
37858 * @param {Roo.LayoutRegion} region The resized region
37859 * @param {Number} newSize The new size (width for east/west, height for north/south)
37861 "regionresized" : true,
37863 * @event regioncollapsed
37864 * Fires when a region is collapsed.
37865 * @param {Roo.LayoutRegion} region The collapsed region
37867 "regioncollapsed" : true,
37869 * @event regionexpanded
37870 * Fires when a region is expanded.
37871 * @param {Roo.LayoutRegion} region The expanded region
37873 "regionexpanded" : true
37875 this.updating = false;
37878 this.el = Roo.get(config.el);
37884 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37889 monitorWindowResize : true,
37895 onRender : function(ct, position)
37898 this.el = Roo.get(ct);
37901 //this.fireEvent('render',this);
37905 initEvents: function()
37909 // ie scrollbar fix
37910 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37911 document.body.scroll = "no";
37912 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37913 this.el.position('relative');
37915 this.id = this.el.id;
37916 this.el.addClass("roo-layout-container");
37917 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37918 if(this.el.dom != document.body ) {
37919 this.el.on('resize', this.layout,this);
37920 this.el.on('show', this.layout,this);
37926 * Returns true if this layout is currently being updated
37927 * @return {Boolean}
37929 isUpdating : function(){
37930 return this.updating;
37934 * Suspend the LayoutManager from doing auto-layouts while
37935 * making multiple add or remove calls
37937 beginUpdate : function(){
37938 this.updating = true;
37942 * Restore auto-layouts and optionally disable the manager from performing a layout
37943 * @param {Boolean} noLayout true to disable a layout update
37945 endUpdate : function(noLayout){
37946 this.updating = false;
37952 layout: function(){
37956 onRegionResized : function(region, newSize){
37957 this.fireEvent("regionresized", region, newSize);
37961 onRegionCollapsed : function(region){
37962 this.fireEvent("regioncollapsed", region);
37965 onRegionExpanded : function(region){
37966 this.fireEvent("regionexpanded", region);
37970 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37971 * performs box-model adjustments.
37972 * @return {Object} The size as an object {width: (the width), height: (the height)}
37974 getViewSize : function()
37977 if(this.el.dom != document.body){
37978 size = this.el.getSize();
37980 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37982 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37983 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37988 * Returns the Element this layout is bound to.
37989 * @return {Roo.Element}
37991 getEl : function(){
37996 * Returns the specified region.
37997 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37998 * @return {Roo.LayoutRegion}
38000 getRegion : function(target){
38001 return this.regions[target.toLowerCase()];
38004 onWindowResize : function(){
38005 if(this.monitorWindowResize){
38012 * Ext JS Library 1.1.1
38013 * Copyright(c) 2006-2007, Ext JS, LLC.
38015 * Originally Released Under LGPL - original licence link has changed is not relivant.
38018 * <script type="text/javascript">
38021 * @class Roo.bootstrap.layout.Border
38022 * @extends Roo.bootstrap.layout.Manager
38024 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38025 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38026 * please see: examples/bootstrap/nested.html<br><br>
38028 <b>The container the layout is rendered into can be either the body element or any other element.
38029 If it is not the body element, the container needs to either be an absolute positioned element,
38030 or you will need to add "position:relative" to the css of the container. You will also need to specify
38031 the container size if it is not the body element.</b>
38034 * Create a new Border
38035 * @param {Object} config Configuration options
38037 Roo.bootstrap.layout.Border = function(config){
38038 config = config || {};
38039 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38043 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38044 if(config[region]){
38045 config[region].region = region;
38046 this.addRegion(config[region]);
38052 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38054 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38057 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38060 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38063 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38066 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38069 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38075 parent : false, // this might point to a 'nest' or a ???
38078 * Creates and adds a new region if it doesn't already exist.
38079 * @param {String} target The target region key (north, south, east, west or center).
38080 * @param {Object} config The regions config object
38081 * @return {BorderLayoutRegion} The new region
38083 addRegion : function(config)
38085 if(!this.regions[config.region]){
38086 var r = this.factory(config);
38087 this.bindRegion(r);
38089 return this.regions[config.region];
38093 bindRegion : function(r){
38094 this.regions[r.config.region] = r;
38096 r.on("visibilitychange", this.layout, this);
38097 r.on("paneladded", this.layout, this);
38098 r.on("panelremoved", this.layout, this);
38099 r.on("invalidated", this.layout, this);
38100 r.on("resized", this.onRegionResized, this);
38101 r.on("collapsed", this.onRegionCollapsed, this);
38102 r.on("expanded", this.onRegionExpanded, this);
38106 * Performs a layout update.
38108 layout : function()
38110 if(this.updating) {
38114 // render all the rebions if they have not been done alreayd?
38115 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38116 if(this.regions[region] && !this.regions[region].bodyEl){
38117 this.regions[region].onRender(this.el)
38121 var size = this.getViewSize();
38122 var w = size.width;
38123 var h = size.height;
38128 //var x = 0, y = 0;
38130 var rs = this.regions;
38131 var north = rs["north"];
38132 var south = rs["south"];
38133 var west = rs["west"];
38134 var east = rs["east"];
38135 var center = rs["center"];
38136 //if(this.hideOnLayout){ // not supported anymore
38137 //c.el.setStyle("display", "none");
38139 if(north && north.isVisible()){
38140 var b = north.getBox();
38141 var m = north.getMargins();
38142 b.width = w - (m.left+m.right);
38145 centerY = b.height + b.y + m.bottom;
38146 centerH -= centerY;
38147 north.updateBox(this.safeBox(b));
38149 if(south && south.isVisible()){
38150 var b = south.getBox();
38151 var m = south.getMargins();
38152 b.width = w - (m.left+m.right);
38154 var totalHeight = (b.height + m.top + m.bottom);
38155 b.y = h - totalHeight + m.top;
38156 centerH -= totalHeight;
38157 south.updateBox(this.safeBox(b));
38159 if(west && west.isVisible()){
38160 var b = west.getBox();
38161 var m = west.getMargins();
38162 b.height = centerH - (m.top+m.bottom);
38164 b.y = centerY + m.top;
38165 var totalWidth = (b.width + m.left + m.right);
38166 centerX += totalWidth;
38167 centerW -= totalWidth;
38168 west.updateBox(this.safeBox(b));
38170 if(east && east.isVisible()){
38171 var b = east.getBox();
38172 var m = east.getMargins();
38173 b.height = centerH - (m.top+m.bottom);
38174 var totalWidth = (b.width + m.left + m.right);
38175 b.x = w - totalWidth + m.left;
38176 b.y = centerY + m.top;
38177 centerW -= totalWidth;
38178 east.updateBox(this.safeBox(b));
38181 var m = center.getMargins();
38183 x: centerX + m.left,
38184 y: centerY + m.top,
38185 width: centerW - (m.left+m.right),
38186 height: centerH - (m.top+m.bottom)
38188 //if(this.hideOnLayout){
38189 //center.el.setStyle("display", "block");
38191 center.updateBox(this.safeBox(centerBox));
38194 this.fireEvent("layout", this);
38198 safeBox : function(box){
38199 box.width = Math.max(0, box.width);
38200 box.height = Math.max(0, box.height);
38205 * Adds a ContentPanel (or subclass) to this layout.
38206 * @param {String} target The target region key (north, south, east, west or center).
38207 * @param {Roo.ContentPanel} panel The panel to add
38208 * @return {Roo.ContentPanel} The added panel
38210 add : function(target, panel){
38212 target = target.toLowerCase();
38213 return this.regions[target].add(panel);
38217 * Remove a ContentPanel (or subclass) to this layout.
38218 * @param {String} target The target region key (north, south, east, west or center).
38219 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38220 * @return {Roo.ContentPanel} The removed panel
38222 remove : function(target, panel){
38223 target = target.toLowerCase();
38224 return this.regions[target].remove(panel);
38228 * Searches all regions for a panel with the specified id
38229 * @param {String} panelId
38230 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38232 findPanel : function(panelId){
38233 var rs = this.regions;
38234 for(var target in rs){
38235 if(typeof rs[target] != "function"){
38236 var p = rs[target].getPanel(panelId);
38246 * Searches all regions for a panel with the specified id and activates (shows) it.
38247 * @param {String/ContentPanel} panelId The panels id or the panel itself
38248 * @return {Roo.ContentPanel} The shown panel or null
38250 showPanel : function(panelId) {
38251 var rs = this.regions;
38252 for(var target in rs){
38253 var r = rs[target];
38254 if(typeof r != "function"){
38255 if(r.hasPanel(panelId)){
38256 return r.showPanel(panelId);
38264 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38265 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38268 restoreState : function(provider){
38270 provider = Roo.state.Manager;
38272 var sm = new Roo.LayoutStateManager();
38273 sm.init(this, provider);
38279 * Adds a xtype elements to the layout.
38283 xtype : 'ContentPanel',
38290 xtype : 'NestedLayoutPanel',
38296 items : [ ... list of content panels or nested layout panels.. ]
38300 * @param {Object} cfg Xtype definition of item to add.
38302 addxtype : function(cfg)
38304 // basically accepts a pannel...
38305 // can accept a layout region..!?!?
38306 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38309 // theory? children can only be panels??
38311 //if (!cfg.xtype.match(/Panel$/)) {
38316 if (typeof(cfg.region) == 'undefined') {
38317 Roo.log("Failed to add Panel, region was not set");
38321 var region = cfg.region;
38327 xitems = cfg.items;
38332 if ( region == 'center') {
38333 Roo.log("Center: " + cfg.title);
38339 case 'Content': // ContentPanel (el, cfg)
38340 case 'Scroll': // ContentPanel (el, cfg)
38342 cfg.autoCreate = cfg.autoCreate || true;
38343 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38345 // var el = this.el.createChild();
38346 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38349 this.add(region, ret);
38353 case 'TreePanel': // our new panel!
38354 cfg.el = this.el.createChild();
38355 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38356 this.add(region, ret);
38361 // create a new Layout (which is a Border Layout...
38363 var clayout = cfg.layout;
38364 clayout.el = this.el.createChild();
38365 clayout.items = clayout.items || [];
38369 // replace this exitems with the clayout ones..
38370 xitems = clayout.items;
38372 // force background off if it's in center...
38373 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38374 cfg.background = false;
38376 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38379 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38380 //console.log('adding nested layout panel ' + cfg.toSource());
38381 this.add(region, ret);
38382 nb = {}; /// find first...
38387 // needs grid and region
38389 //var el = this.getRegion(region).el.createChild();
38391 *var el = this.el.createChild();
38392 // create the grid first...
38393 cfg.grid.container = el;
38394 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38397 if (region == 'center' && this.active ) {
38398 cfg.background = false;
38401 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38403 this.add(region, ret);
38405 if (cfg.background) {
38406 // render grid on panel activation (if panel background)
38407 ret.on('activate', function(gp) {
38408 if (!gp.grid.rendered) {
38409 // gp.grid.render(el);
38413 // cfg.grid.render(el);
38419 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38420 // it was the old xcomponent building that caused this before.
38421 // espeically if border is the top element in the tree.
38431 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38433 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38434 this.add(region, ret);
38438 throw "Can not add '" + cfg.xtype + "' to Border";
38444 this.beginUpdate();
38448 Roo.each(xitems, function(i) {
38449 region = nb && i.region ? i.region : false;
38451 var add = ret.addxtype(i);
38454 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38455 if (!i.background) {
38456 abn[region] = nb[region] ;
38463 // make the last non-background panel active..
38464 //if (nb) { Roo.log(abn); }
38467 for(var r in abn) {
38468 region = this.getRegion(r);
38470 // tried using nb[r], but it does not work..
38472 region.showPanel(abn[r]);
38483 factory : function(cfg)
38486 var validRegions = Roo.bootstrap.layout.Border.regions;
38488 var target = cfg.region;
38491 var r = Roo.bootstrap.layout;
38495 return new r.North(cfg);
38497 return new r.South(cfg);
38499 return new r.East(cfg);
38501 return new r.West(cfg);
38503 return new r.Center(cfg);
38505 throw 'Layout region "'+target+'" not supported.';
38512 * Ext JS Library 1.1.1
38513 * Copyright(c) 2006-2007, Ext JS, LLC.
38515 * Originally Released Under LGPL - original licence link has changed is not relivant.
38518 * <script type="text/javascript">
38522 * @class Roo.bootstrap.layout.Basic
38523 * @extends Roo.util.Observable
38524 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38525 * and does not have a titlebar, tabs or any other features. All it does is size and position
38526 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38527 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38528 * @cfg {string} region the region that it inhabits..
38529 * @cfg {bool} skipConfig skip config?
38533 Roo.bootstrap.layout.Basic = function(config){
38535 this.mgr = config.mgr;
38537 this.position = config.region;
38539 var skipConfig = config.skipConfig;
38543 * @scope Roo.BasicLayoutRegion
38547 * @event beforeremove
38548 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38549 * @param {Roo.LayoutRegion} this
38550 * @param {Roo.ContentPanel} panel The panel
38551 * @param {Object} e The cancel event object
38553 "beforeremove" : true,
38555 * @event invalidated
38556 * Fires when the layout for this region is changed.
38557 * @param {Roo.LayoutRegion} this
38559 "invalidated" : true,
38561 * @event visibilitychange
38562 * Fires when this region is shown or hidden
38563 * @param {Roo.LayoutRegion} this
38564 * @param {Boolean} visibility true or false
38566 "visibilitychange" : true,
38568 * @event paneladded
38569 * Fires when a panel is added.
38570 * @param {Roo.LayoutRegion} this
38571 * @param {Roo.ContentPanel} panel The panel
38573 "paneladded" : true,
38575 * @event panelremoved
38576 * Fires when a panel is removed.
38577 * @param {Roo.LayoutRegion} this
38578 * @param {Roo.ContentPanel} panel The panel
38580 "panelremoved" : true,
38582 * @event beforecollapse
38583 * Fires when this region before collapse.
38584 * @param {Roo.LayoutRegion} this
38586 "beforecollapse" : true,
38589 * Fires when this region is collapsed.
38590 * @param {Roo.LayoutRegion} this
38592 "collapsed" : true,
38595 * Fires when this region is expanded.
38596 * @param {Roo.LayoutRegion} this
38601 * Fires when this region is slid into view.
38602 * @param {Roo.LayoutRegion} this
38604 "slideshow" : true,
38607 * Fires when this region slides out of view.
38608 * @param {Roo.LayoutRegion} this
38610 "slidehide" : true,
38612 * @event panelactivated
38613 * Fires when a panel is activated.
38614 * @param {Roo.LayoutRegion} this
38615 * @param {Roo.ContentPanel} panel The activated panel
38617 "panelactivated" : true,
38620 * Fires when the user resizes this region.
38621 * @param {Roo.LayoutRegion} this
38622 * @param {Number} newSize The new size (width for east/west, height for north/south)
38626 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38627 this.panels = new Roo.util.MixedCollection();
38628 this.panels.getKey = this.getPanelId.createDelegate(this);
38630 this.activePanel = null;
38631 // ensure listeners are added...
38633 if (config.listeners || config.events) {
38634 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38635 listeners : config.listeners || {},
38636 events : config.events || {}
38640 if(skipConfig !== true){
38641 this.applyConfig(config);
38645 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38647 getPanelId : function(p){
38651 applyConfig : function(config){
38652 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38653 this.config = config;
38658 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38659 * the width, for horizontal (north, south) the height.
38660 * @param {Number} newSize The new width or height
38662 resizeTo : function(newSize){
38663 var el = this.el ? this.el :
38664 (this.activePanel ? this.activePanel.getEl() : null);
38666 switch(this.position){
38669 el.setWidth(newSize);
38670 this.fireEvent("resized", this, newSize);
38674 el.setHeight(newSize);
38675 this.fireEvent("resized", this, newSize);
38681 getBox : function(){
38682 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38685 getMargins : function(){
38686 return this.margins;
38689 updateBox : function(box){
38691 var el = this.activePanel.getEl();
38692 el.dom.style.left = box.x + "px";
38693 el.dom.style.top = box.y + "px";
38694 this.activePanel.setSize(box.width, box.height);
38698 * Returns the container element for this region.
38699 * @return {Roo.Element}
38701 getEl : function(){
38702 return this.activePanel;
38706 * Returns true if this region is currently visible.
38707 * @return {Boolean}
38709 isVisible : function(){
38710 return this.activePanel ? true : false;
38713 setActivePanel : function(panel){
38714 panel = this.getPanel(panel);
38715 if(this.activePanel && this.activePanel != panel){
38716 this.activePanel.setActiveState(false);
38717 this.activePanel.getEl().setLeftTop(-10000,-10000);
38719 this.activePanel = panel;
38720 panel.setActiveState(true);
38722 panel.setSize(this.box.width, this.box.height);
38724 this.fireEvent("panelactivated", this, panel);
38725 this.fireEvent("invalidated");
38729 * Show the specified panel.
38730 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38731 * @return {Roo.ContentPanel} The shown panel or null
38733 showPanel : function(panel){
38734 panel = this.getPanel(panel);
38736 this.setActivePanel(panel);
38742 * Get the active panel for this region.
38743 * @return {Roo.ContentPanel} The active panel or null
38745 getActivePanel : function(){
38746 return this.activePanel;
38750 * Add the passed ContentPanel(s)
38751 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38752 * @return {Roo.ContentPanel} The panel added (if only one was added)
38754 add : function(panel){
38755 if(arguments.length > 1){
38756 for(var i = 0, len = arguments.length; i < len; i++) {
38757 this.add(arguments[i]);
38761 if(this.hasPanel(panel)){
38762 this.showPanel(panel);
38765 var el = panel.getEl();
38766 if(el.dom.parentNode != this.mgr.el.dom){
38767 this.mgr.el.dom.appendChild(el.dom);
38769 if(panel.setRegion){
38770 panel.setRegion(this);
38772 this.panels.add(panel);
38773 el.setStyle("position", "absolute");
38774 if(!panel.background){
38775 this.setActivePanel(panel);
38776 if(this.config.initialSize && this.panels.getCount()==1){
38777 this.resizeTo(this.config.initialSize);
38780 this.fireEvent("paneladded", this, panel);
38785 * Returns true if the panel is in this region.
38786 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38787 * @return {Boolean}
38789 hasPanel : function(panel){
38790 if(typeof panel == "object"){ // must be panel obj
38791 panel = panel.getId();
38793 return this.getPanel(panel) ? true : false;
38797 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38798 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38799 * @param {Boolean} preservePanel Overrides the config preservePanel option
38800 * @return {Roo.ContentPanel} The panel that was removed
38802 remove : function(panel, preservePanel){
38803 panel = this.getPanel(panel);
38808 this.fireEvent("beforeremove", this, panel, e);
38809 if(e.cancel === true){
38812 var panelId = panel.getId();
38813 this.panels.removeKey(panelId);
38818 * Returns the panel specified or null if it's not in this region.
38819 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38820 * @return {Roo.ContentPanel}
38822 getPanel : function(id){
38823 if(typeof id == "object"){ // must be panel obj
38826 return this.panels.get(id);
38830 * Returns this regions position (north/south/east/west/center).
38833 getPosition: function(){
38834 return this.position;
38838 * Ext JS Library 1.1.1
38839 * Copyright(c) 2006-2007, Ext JS, LLC.
38841 * Originally Released Under LGPL - original licence link has changed is not relivant.
38844 * <script type="text/javascript">
38848 * @class Roo.bootstrap.layout.Region
38849 * @extends Roo.bootstrap.layout.Basic
38850 * This class represents a region in a layout manager.
38852 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38853 * @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})
38854 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38855 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38856 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38857 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38858 * @cfg {String} title The title for the region (overrides panel titles)
38859 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38860 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38861 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38862 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38863 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38864 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38865 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38866 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38867 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38868 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38870 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38871 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38872 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38873 * @cfg {Number} width For East/West panels
38874 * @cfg {Number} height For North/South panels
38875 * @cfg {Boolean} split To show the splitter
38876 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38878 * @cfg {string} cls Extra CSS classes to add to region
38880 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38881 * @cfg {string} region the region that it inhabits..
38884 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38885 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38887 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38888 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38889 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38891 Roo.bootstrap.layout.Region = function(config)
38893 this.applyConfig(config);
38895 var mgr = config.mgr;
38896 var pos = config.region;
38897 config.skipConfig = true;
38898 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38901 this.onRender(mgr.el);
38904 this.visible = true;
38905 this.collapsed = false;
38906 this.unrendered_panels = [];
38909 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38911 position: '', // set by wrapper (eg. north/south etc..)
38912 unrendered_panels : null, // unrendered panels.
38914 tabPosition : false,
38916 mgr: false, // points to 'Border'
38919 createBody : function(){
38920 /** This region's body element
38921 * @type Roo.Element */
38922 this.bodyEl = this.el.createChild({
38924 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38928 onRender: function(ctr, pos)
38930 var dh = Roo.DomHelper;
38931 /** This region's container element
38932 * @type Roo.Element */
38933 this.el = dh.append(ctr.dom, {
38935 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38937 /** This region's title element
38938 * @type Roo.Element */
38940 this.titleEl = dh.append(this.el.dom, {
38942 unselectable: "on",
38943 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38945 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38946 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38950 this.titleEl.enableDisplayMode();
38951 /** This region's title text element
38952 * @type HTMLElement */
38953 this.titleTextEl = this.titleEl.dom.firstChild;
38954 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38956 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38957 this.closeBtn.enableDisplayMode();
38958 this.closeBtn.on("click", this.closeClicked, this);
38959 this.closeBtn.hide();
38961 this.createBody(this.config);
38962 if(this.config.hideWhenEmpty){
38964 this.on("paneladded", this.validateVisibility, this);
38965 this.on("panelremoved", this.validateVisibility, this);
38967 if(this.autoScroll){
38968 this.bodyEl.setStyle("overflow", "auto");
38970 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38972 //if(c.titlebar !== false){
38973 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38974 this.titleEl.hide();
38976 this.titleEl.show();
38977 if(this.config.title){
38978 this.titleTextEl.innerHTML = this.config.title;
38982 if(this.config.collapsed){
38983 this.collapse(true);
38985 if(this.config.hidden){
38989 if (this.unrendered_panels && this.unrendered_panels.length) {
38990 for (var i =0;i< this.unrendered_panels.length; i++) {
38991 this.add(this.unrendered_panels[i]);
38993 this.unrendered_panels = null;
38999 applyConfig : function(c)
39002 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39003 var dh = Roo.DomHelper;
39004 if(c.titlebar !== false){
39005 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39006 this.collapseBtn.on("click", this.collapse, this);
39007 this.collapseBtn.enableDisplayMode();
39009 if(c.showPin === true || this.showPin){
39010 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39011 this.stickBtn.enableDisplayMode();
39012 this.stickBtn.on("click", this.expand, this);
39013 this.stickBtn.hide();
39018 /** This region's collapsed element
39019 * @type Roo.Element */
39022 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39023 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39026 if(c.floatable !== false){
39027 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39028 this.collapsedEl.on("click", this.collapseClick, this);
39031 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39032 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39033 id: "message", unselectable: "on", style:{"float":"left"}});
39034 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39036 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39037 this.expandBtn.on("click", this.expand, this);
39041 if(this.collapseBtn){
39042 this.collapseBtn.setVisible(c.collapsible == true);
39045 this.cmargins = c.cmargins || this.cmargins ||
39046 (this.position == "west" || this.position == "east" ?
39047 {top: 0, left: 2, right:2, bottom: 0} :
39048 {top: 2, left: 0, right:0, bottom: 2});
39050 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39053 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39055 this.autoScroll = c.autoScroll || false;
39060 this.duration = c.duration || .30;
39061 this.slideDuration = c.slideDuration || .45;
39066 * Returns true if this region is currently visible.
39067 * @return {Boolean}
39069 isVisible : function(){
39070 return this.visible;
39074 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39075 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39077 //setCollapsedTitle : function(title){
39078 // title = title || " ";
39079 // if(this.collapsedTitleTextEl){
39080 // this.collapsedTitleTextEl.innerHTML = title;
39084 getBox : function(){
39086 // if(!this.collapsed){
39087 b = this.el.getBox(false, true);
39089 // b = this.collapsedEl.getBox(false, true);
39094 getMargins : function(){
39095 return this.margins;
39096 //return this.collapsed ? this.cmargins : this.margins;
39099 highlight : function(){
39100 this.el.addClass("x-layout-panel-dragover");
39103 unhighlight : function(){
39104 this.el.removeClass("x-layout-panel-dragover");
39107 updateBox : function(box)
39109 if (!this.bodyEl) {
39110 return; // not rendered yet..
39114 if(!this.collapsed){
39115 this.el.dom.style.left = box.x + "px";
39116 this.el.dom.style.top = box.y + "px";
39117 this.updateBody(box.width, box.height);
39119 this.collapsedEl.dom.style.left = box.x + "px";
39120 this.collapsedEl.dom.style.top = box.y + "px";
39121 this.collapsedEl.setSize(box.width, box.height);
39124 this.tabs.autoSizeTabs();
39128 updateBody : function(w, h)
39131 this.el.setWidth(w);
39132 w -= this.el.getBorderWidth("rl");
39133 if(this.config.adjustments){
39134 w += this.config.adjustments[0];
39137 if(h !== null && h > 0){
39138 this.el.setHeight(h);
39139 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39140 h -= this.el.getBorderWidth("tb");
39141 if(this.config.adjustments){
39142 h += this.config.adjustments[1];
39144 this.bodyEl.setHeight(h);
39146 h = this.tabs.syncHeight(h);
39149 if(this.panelSize){
39150 w = w !== null ? w : this.panelSize.width;
39151 h = h !== null ? h : this.panelSize.height;
39153 if(this.activePanel){
39154 var el = this.activePanel.getEl();
39155 w = w !== null ? w : el.getWidth();
39156 h = h !== null ? h : el.getHeight();
39157 this.panelSize = {width: w, height: h};
39158 this.activePanel.setSize(w, h);
39160 if(Roo.isIE && this.tabs){
39161 this.tabs.el.repaint();
39166 * Returns the container element for this region.
39167 * @return {Roo.Element}
39169 getEl : function(){
39174 * Hides this region.
39177 //if(!this.collapsed){
39178 this.el.dom.style.left = "-2000px";
39181 // this.collapsedEl.dom.style.left = "-2000px";
39182 // this.collapsedEl.hide();
39184 this.visible = false;
39185 this.fireEvent("visibilitychange", this, false);
39189 * Shows this region if it was previously hidden.
39192 //if(!this.collapsed){
39195 // this.collapsedEl.show();
39197 this.visible = true;
39198 this.fireEvent("visibilitychange", this, true);
39201 closeClicked : function(){
39202 if(this.activePanel){
39203 this.remove(this.activePanel);
39207 collapseClick : function(e){
39209 e.stopPropagation();
39212 e.stopPropagation();
39218 * Collapses this region.
39219 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39222 collapse : function(skipAnim, skipCheck = false){
39223 if(this.collapsed) {
39227 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39229 this.collapsed = true;
39231 this.split.el.hide();
39233 if(this.config.animate && skipAnim !== true){
39234 this.fireEvent("invalidated", this);
39235 this.animateCollapse();
39237 this.el.setLocation(-20000,-20000);
39239 this.collapsedEl.show();
39240 this.fireEvent("collapsed", this);
39241 this.fireEvent("invalidated", this);
39247 animateCollapse : function(){
39252 * Expands this region if it was previously collapsed.
39253 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39254 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39257 expand : function(e, skipAnim){
39259 e.stopPropagation();
39261 if(!this.collapsed || this.el.hasActiveFx()) {
39265 this.afterSlideIn();
39268 this.collapsed = false;
39269 if(this.config.animate && skipAnim !== true){
39270 this.animateExpand();
39274 this.split.el.show();
39276 this.collapsedEl.setLocation(-2000,-2000);
39277 this.collapsedEl.hide();
39278 this.fireEvent("invalidated", this);
39279 this.fireEvent("expanded", this);
39283 animateExpand : function(){
39287 initTabs : function()
39289 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39291 var ts = new Roo.bootstrap.panel.Tabs({
39292 el: this.bodyEl.dom,
39294 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39295 disableTooltips: this.config.disableTabTips,
39296 toolbar : this.config.toolbar
39299 if(this.config.hideTabs){
39300 ts.stripWrap.setDisplayed(false);
39303 ts.resizeTabs = this.config.resizeTabs === true;
39304 ts.minTabWidth = this.config.minTabWidth || 40;
39305 ts.maxTabWidth = this.config.maxTabWidth || 250;
39306 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39307 ts.monitorResize = false;
39308 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39309 ts.bodyEl.addClass('roo-layout-tabs-body');
39310 this.panels.each(this.initPanelAsTab, this);
39313 initPanelAsTab : function(panel){
39314 var ti = this.tabs.addTab(
39318 this.config.closeOnTab && panel.isClosable(),
39321 if(panel.tabTip !== undefined){
39322 ti.setTooltip(panel.tabTip);
39324 ti.on("activate", function(){
39325 this.setActivePanel(panel);
39328 if(this.config.closeOnTab){
39329 ti.on("beforeclose", function(t, e){
39331 this.remove(panel);
39335 panel.tabItem = ti;
39340 updatePanelTitle : function(panel, title)
39342 if(this.activePanel == panel){
39343 this.updateTitle(title);
39346 var ti = this.tabs.getTab(panel.getEl().id);
39348 if(panel.tabTip !== undefined){
39349 ti.setTooltip(panel.tabTip);
39354 updateTitle : function(title){
39355 if(this.titleTextEl && !this.config.title){
39356 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39360 setActivePanel : function(panel)
39362 panel = this.getPanel(panel);
39363 if(this.activePanel && this.activePanel != panel){
39364 if(this.activePanel.setActiveState(false) === false){
39368 this.activePanel = panel;
39369 panel.setActiveState(true);
39370 if(this.panelSize){
39371 panel.setSize(this.panelSize.width, this.panelSize.height);
39374 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39376 this.updateTitle(panel.getTitle());
39378 this.fireEvent("invalidated", this);
39380 this.fireEvent("panelactivated", this, panel);
39384 * Shows the specified panel.
39385 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39386 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39388 showPanel : function(panel)
39390 panel = this.getPanel(panel);
39393 var tab = this.tabs.getTab(panel.getEl().id);
39394 if(tab.isHidden()){
39395 this.tabs.unhideTab(tab.id);
39399 this.setActivePanel(panel);
39406 * Get the active panel for this region.
39407 * @return {Roo.ContentPanel} The active panel or null
39409 getActivePanel : function(){
39410 return this.activePanel;
39413 validateVisibility : function(){
39414 if(this.panels.getCount() < 1){
39415 this.updateTitle(" ");
39416 this.closeBtn.hide();
39419 if(!this.isVisible()){
39426 * Adds the passed ContentPanel(s) to this region.
39427 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39428 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39430 add : function(panel)
39432 if(arguments.length > 1){
39433 for(var i = 0, len = arguments.length; i < len; i++) {
39434 this.add(arguments[i]);
39439 // if we have not been rendered yet, then we can not really do much of this..
39440 if (!this.bodyEl) {
39441 this.unrendered_panels.push(panel);
39448 if(this.hasPanel(panel)){
39449 this.showPanel(panel);
39452 panel.setRegion(this);
39453 this.panels.add(panel);
39454 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39455 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39456 // and hide them... ???
39457 this.bodyEl.dom.appendChild(panel.getEl().dom);
39458 if(panel.background !== true){
39459 this.setActivePanel(panel);
39461 this.fireEvent("paneladded", this, panel);
39468 this.initPanelAsTab(panel);
39472 if(panel.background !== true){
39473 this.tabs.activate(panel.getEl().id);
39475 this.fireEvent("paneladded", this, panel);
39480 * Hides the tab for the specified panel.
39481 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39483 hidePanel : function(panel){
39484 if(this.tabs && (panel = this.getPanel(panel))){
39485 this.tabs.hideTab(panel.getEl().id);
39490 * Unhides the tab for a previously hidden panel.
39491 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39493 unhidePanel : function(panel){
39494 if(this.tabs && (panel = this.getPanel(panel))){
39495 this.tabs.unhideTab(panel.getEl().id);
39499 clearPanels : function(){
39500 while(this.panels.getCount() > 0){
39501 this.remove(this.panels.first());
39506 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39507 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39508 * @param {Boolean} preservePanel Overrides the config preservePanel option
39509 * @return {Roo.ContentPanel} The panel that was removed
39511 remove : function(panel, preservePanel)
39513 panel = this.getPanel(panel);
39518 this.fireEvent("beforeremove", this, panel, e);
39519 if(e.cancel === true){
39522 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39523 var panelId = panel.getId();
39524 this.panels.removeKey(panelId);
39526 document.body.appendChild(panel.getEl().dom);
39529 this.tabs.removeTab(panel.getEl().id);
39530 }else if (!preservePanel){
39531 this.bodyEl.dom.removeChild(panel.getEl().dom);
39533 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39534 var p = this.panels.first();
39535 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39536 tempEl.appendChild(p.getEl().dom);
39537 this.bodyEl.update("");
39538 this.bodyEl.dom.appendChild(p.getEl().dom);
39540 this.updateTitle(p.getTitle());
39542 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39543 this.setActivePanel(p);
39545 panel.setRegion(null);
39546 if(this.activePanel == panel){
39547 this.activePanel = null;
39549 if(this.config.autoDestroy !== false && preservePanel !== true){
39550 try{panel.destroy();}catch(e){}
39552 this.fireEvent("panelremoved", this, panel);
39557 * Returns the TabPanel component used by this region
39558 * @return {Roo.TabPanel}
39560 getTabs : function(){
39564 createTool : function(parentEl, className){
39565 var btn = Roo.DomHelper.append(parentEl, {
39567 cls: "x-layout-tools-button",
39570 cls: "roo-layout-tools-button-inner " + className,
39574 btn.addClassOnOver("roo-layout-tools-button-over");
39579 * Ext JS Library 1.1.1
39580 * Copyright(c) 2006-2007, Ext JS, LLC.
39582 * Originally Released Under LGPL - original licence link has changed is not relivant.
39585 * <script type="text/javascript">
39591 * @class Roo.SplitLayoutRegion
39592 * @extends Roo.LayoutRegion
39593 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39595 Roo.bootstrap.layout.Split = function(config){
39596 this.cursor = config.cursor;
39597 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39600 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39602 splitTip : "Drag to resize.",
39603 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39604 useSplitTips : false,
39606 applyConfig : function(config){
39607 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39610 onRender : function(ctr,pos) {
39612 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39613 if(!this.config.split){
39618 var splitEl = Roo.DomHelper.append(ctr.dom, {
39620 id: this.el.id + "-split",
39621 cls: "roo-layout-split roo-layout-split-"+this.position,
39624 /** The SplitBar for this region
39625 * @type Roo.SplitBar */
39626 // does not exist yet...
39627 Roo.log([this.position, this.orientation]);
39629 this.split = new Roo.bootstrap.SplitBar({
39630 dragElement : splitEl,
39631 resizingElement: this.el,
39632 orientation : this.orientation
39635 this.split.on("moved", this.onSplitMove, this);
39636 this.split.useShim = this.config.useShim === true;
39637 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39638 if(this.useSplitTips){
39639 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39641 //if(config.collapsible){
39642 // this.split.el.on("dblclick", this.collapse, this);
39645 if(typeof this.config.minSize != "undefined"){
39646 this.split.minSize = this.config.minSize;
39648 if(typeof this.config.maxSize != "undefined"){
39649 this.split.maxSize = this.config.maxSize;
39651 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39652 this.hideSplitter();
39657 getHMaxSize : function(){
39658 var cmax = this.config.maxSize || 10000;
39659 var center = this.mgr.getRegion("center");
39660 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39663 getVMaxSize : function(){
39664 var cmax = this.config.maxSize || 10000;
39665 var center = this.mgr.getRegion("center");
39666 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39669 onSplitMove : function(split, newSize){
39670 this.fireEvent("resized", this, newSize);
39674 * Returns the {@link Roo.SplitBar} for this region.
39675 * @return {Roo.SplitBar}
39677 getSplitBar : function(){
39682 this.hideSplitter();
39683 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39686 hideSplitter : function(){
39688 this.split.el.setLocation(-2000,-2000);
39689 this.split.el.hide();
39695 this.split.el.show();
39697 Roo.bootstrap.layout.Split.superclass.show.call(this);
39700 beforeSlide: function(){
39701 if(Roo.isGecko){// firefox overflow auto bug workaround
39702 this.bodyEl.clip();
39704 this.tabs.bodyEl.clip();
39706 if(this.activePanel){
39707 this.activePanel.getEl().clip();
39709 if(this.activePanel.beforeSlide){
39710 this.activePanel.beforeSlide();
39716 afterSlide : function(){
39717 if(Roo.isGecko){// firefox overflow auto bug workaround
39718 this.bodyEl.unclip();
39720 this.tabs.bodyEl.unclip();
39722 if(this.activePanel){
39723 this.activePanel.getEl().unclip();
39724 if(this.activePanel.afterSlide){
39725 this.activePanel.afterSlide();
39731 initAutoHide : function(){
39732 if(this.autoHide !== false){
39733 if(!this.autoHideHd){
39734 var st = new Roo.util.DelayedTask(this.slideIn, this);
39735 this.autoHideHd = {
39736 "mouseout": function(e){
39737 if(!e.within(this.el, true)){
39741 "mouseover" : function(e){
39747 this.el.on(this.autoHideHd);
39751 clearAutoHide : function(){
39752 if(this.autoHide !== false){
39753 this.el.un("mouseout", this.autoHideHd.mouseout);
39754 this.el.un("mouseover", this.autoHideHd.mouseover);
39758 clearMonitor : function(){
39759 Roo.get(document).un("click", this.slideInIf, this);
39762 // these names are backwards but not changed for compat
39763 slideOut : function(){
39764 if(this.isSlid || this.el.hasActiveFx()){
39767 this.isSlid = true;
39768 if(this.collapseBtn){
39769 this.collapseBtn.hide();
39771 this.closeBtnState = this.closeBtn.getStyle('display');
39772 this.closeBtn.hide();
39774 this.stickBtn.show();
39777 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39778 this.beforeSlide();
39779 this.el.setStyle("z-index", 10001);
39780 this.el.slideIn(this.getSlideAnchor(), {
39781 callback: function(){
39783 this.initAutoHide();
39784 Roo.get(document).on("click", this.slideInIf, this);
39785 this.fireEvent("slideshow", this);
39792 afterSlideIn : function(){
39793 this.clearAutoHide();
39794 this.isSlid = false;
39795 this.clearMonitor();
39796 this.el.setStyle("z-index", "");
39797 if(this.collapseBtn){
39798 this.collapseBtn.show();
39800 this.closeBtn.setStyle('display', this.closeBtnState);
39802 this.stickBtn.hide();
39804 this.fireEvent("slidehide", this);
39807 slideIn : function(cb){
39808 if(!this.isSlid || this.el.hasActiveFx()){
39812 this.isSlid = false;
39813 this.beforeSlide();
39814 this.el.slideOut(this.getSlideAnchor(), {
39815 callback: function(){
39816 this.el.setLeftTop(-10000, -10000);
39818 this.afterSlideIn();
39826 slideInIf : function(e){
39827 if(!e.within(this.el)){
39832 animateCollapse : function(){
39833 this.beforeSlide();
39834 this.el.setStyle("z-index", 20000);
39835 var anchor = this.getSlideAnchor();
39836 this.el.slideOut(anchor, {
39837 callback : function(){
39838 this.el.setStyle("z-index", "");
39839 this.collapsedEl.slideIn(anchor, {duration:.3});
39841 this.el.setLocation(-10000,-10000);
39843 this.fireEvent("collapsed", this);
39850 animateExpand : function(){
39851 this.beforeSlide();
39852 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39853 this.el.setStyle("z-index", 20000);
39854 this.collapsedEl.hide({
39857 this.el.slideIn(this.getSlideAnchor(), {
39858 callback : function(){
39859 this.el.setStyle("z-index", "");
39862 this.split.el.show();
39864 this.fireEvent("invalidated", this);
39865 this.fireEvent("expanded", this);
39893 getAnchor : function(){
39894 return this.anchors[this.position];
39897 getCollapseAnchor : function(){
39898 return this.canchors[this.position];
39901 getSlideAnchor : function(){
39902 return this.sanchors[this.position];
39905 getAlignAdj : function(){
39906 var cm = this.cmargins;
39907 switch(this.position){
39923 getExpandAdj : function(){
39924 var c = this.collapsedEl, cm = this.cmargins;
39925 switch(this.position){
39927 return [-(cm.right+c.getWidth()+cm.left), 0];
39930 return [cm.right+c.getWidth()+cm.left, 0];
39933 return [0, -(cm.top+cm.bottom+c.getHeight())];
39936 return [0, cm.top+cm.bottom+c.getHeight()];
39942 * Ext JS Library 1.1.1
39943 * Copyright(c) 2006-2007, Ext JS, LLC.
39945 * Originally Released Under LGPL - original licence link has changed is not relivant.
39948 * <script type="text/javascript">
39951 * These classes are private internal classes
39953 Roo.bootstrap.layout.Center = function(config){
39954 config.region = "center";
39955 Roo.bootstrap.layout.Region.call(this, config);
39956 this.visible = true;
39957 this.minWidth = config.minWidth || 20;
39958 this.minHeight = config.minHeight || 20;
39961 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39963 // center panel can't be hidden
39967 // center panel can't be hidden
39970 getMinWidth: function(){
39971 return this.minWidth;
39974 getMinHeight: function(){
39975 return this.minHeight;
39989 Roo.bootstrap.layout.North = function(config)
39991 config.region = 'north';
39992 config.cursor = 'n-resize';
39994 Roo.bootstrap.layout.Split.call(this, config);
39998 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39999 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40000 this.split.el.addClass("roo-layout-split-v");
40002 //var size = config.initialSize || config.height;
40003 //if(this.el && typeof size != "undefined"){
40004 // this.el.setHeight(size);
40007 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40009 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40012 onRender : function(ctr, pos)
40014 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40015 var size = this.config.initialSize || this.config.height;
40016 if(this.el && typeof size != "undefined"){
40017 this.el.setHeight(size);
40022 getBox : function(){
40023 if(this.collapsed){
40024 return this.collapsedEl.getBox();
40026 var box = this.el.getBox();
40028 box.height += this.split.el.getHeight();
40033 updateBox : function(box){
40034 if(this.split && !this.collapsed){
40035 box.height -= this.split.el.getHeight();
40036 this.split.el.setLeft(box.x);
40037 this.split.el.setTop(box.y+box.height);
40038 this.split.el.setWidth(box.width);
40040 if(this.collapsed){
40041 this.updateBody(box.width, null);
40043 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40051 Roo.bootstrap.layout.South = function(config){
40052 config.region = 'south';
40053 config.cursor = 's-resize';
40054 Roo.bootstrap.layout.Split.call(this, config);
40056 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40057 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40058 this.split.el.addClass("roo-layout-split-v");
40063 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40064 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40066 onRender : function(ctr, pos)
40068 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40069 var size = this.config.initialSize || this.config.height;
40070 if(this.el && typeof size != "undefined"){
40071 this.el.setHeight(size);
40076 getBox : function(){
40077 if(this.collapsed){
40078 return this.collapsedEl.getBox();
40080 var box = this.el.getBox();
40082 var sh = this.split.el.getHeight();
40089 updateBox : function(box){
40090 if(this.split && !this.collapsed){
40091 var sh = this.split.el.getHeight();
40094 this.split.el.setLeft(box.x);
40095 this.split.el.setTop(box.y-sh);
40096 this.split.el.setWidth(box.width);
40098 if(this.collapsed){
40099 this.updateBody(box.width, null);
40101 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40105 Roo.bootstrap.layout.East = function(config){
40106 config.region = "east";
40107 config.cursor = "e-resize";
40108 Roo.bootstrap.layout.Split.call(this, config);
40110 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40111 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40112 this.split.el.addClass("roo-layout-split-h");
40116 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40117 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40119 onRender : function(ctr, pos)
40121 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40122 var size = this.config.initialSize || this.config.width;
40123 if(this.el && typeof size != "undefined"){
40124 this.el.setWidth(size);
40129 getBox : function(){
40130 if(this.collapsed){
40131 return this.collapsedEl.getBox();
40133 var box = this.el.getBox();
40135 var sw = this.split.el.getWidth();
40142 updateBox : function(box){
40143 if(this.split && !this.collapsed){
40144 var sw = this.split.el.getWidth();
40146 this.split.el.setLeft(box.x);
40147 this.split.el.setTop(box.y);
40148 this.split.el.setHeight(box.height);
40151 if(this.collapsed){
40152 this.updateBody(null, box.height);
40154 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40158 Roo.bootstrap.layout.West = function(config){
40159 config.region = "west";
40160 config.cursor = "w-resize";
40162 Roo.bootstrap.layout.Split.call(this, config);
40164 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40165 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40166 this.split.el.addClass("roo-layout-split-h");
40170 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40171 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40173 onRender: function(ctr, pos)
40175 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40176 var size = this.config.initialSize || this.config.width;
40177 if(typeof size != "undefined"){
40178 this.el.setWidth(size);
40182 getBox : function(){
40183 if(this.collapsed){
40184 return this.collapsedEl.getBox();
40186 var box = this.el.getBox();
40187 if (box.width == 0) {
40188 box.width = this.config.width; // kludge?
40191 box.width += this.split.el.getWidth();
40196 updateBox : function(box){
40197 if(this.split && !this.collapsed){
40198 var sw = this.split.el.getWidth();
40200 this.split.el.setLeft(box.x+box.width);
40201 this.split.el.setTop(box.y);
40202 this.split.el.setHeight(box.height);
40204 if(this.collapsed){
40205 this.updateBody(null, box.height);
40207 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40209 });Roo.namespace("Roo.bootstrap.panel");/*
40211 * Ext JS Library 1.1.1
40212 * Copyright(c) 2006-2007, Ext JS, LLC.
40214 * Originally Released Under LGPL - original licence link has changed is not relivant.
40217 * <script type="text/javascript">
40220 * @class Roo.bootstrap.paenl.Content
40221 * @extends Roo.util.Observable
40223 * @children Roo.bootstrap.Component
40224 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40225 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40226 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40227 * @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
40228 * @cfg {Boolean} closable True if the panel can be closed/removed
40229 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40230 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40231 * @cfg {Toolbar} toolbar A toolbar for this panel
40232 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40233 * @cfg {String} title The title for this panel
40234 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40235 * @cfg {String} url Calls {@link #setUrl} with this value
40236 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40237 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40238 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40239 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40240 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40241 * @cfg {Boolean} badges render the badges
40242 * @cfg {String} cls extra classes to use
40243 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40246 * Create a new ContentPanel.
40247 * @param {String/Object} config A string to set only the title or a config object
40250 Roo.bootstrap.panel.Content = function( config){
40252 this.tpl = config.tpl || false;
40254 var el = config.el;
40255 var content = config.content;
40257 if(config.autoCreate){ // xtype is available if this is called from factory
40260 this.el = Roo.get(el);
40261 if(!this.el && config && config.autoCreate){
40262 if(typeof config.autoCreate == "object"){
40263 if(!config.autoCreate.id){
40264 config.autoCreate.id = config.id||el;
40266 this.el = Roo.DomHelper.append(document.body,
40267 config.autoCreate, true);
40271 cls: (config.cls || '') +
40272 (config.background ? ' bg-' + config.background : '') +
40273 " roo-layout-inactive-content",
40276 if (config.iframe) {
40280 style : 'border: 0px',
40281 src : 'about:blank'
40287 elcfg.html = config.html;
40291 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40292 if (config.iframe) {
40293 this.iframeEl = this.el.select('iframe',true).first();
40298 this.closable = false;
40299 this.loaded = false;
40300 this.active = false;
40303 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40305 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40307 this.wrapEl = this.el; //this.el.wrap();
40309 if (config.toolbar.items) {
40310 ti = config.toolbar.items ;
40311 delete config.toolbar.items ;
40315 this.toolbar.render(this.wrapEl, 'before');
40316 for(var i =0;i < ti.length;i++) {
40317 // Roo.log(['add child', items[i]]);
40318 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40320 this.toolbar.items = nitems;
40321 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40322 delete config.toolbar;
40326 // xtype created footer. - not sure if will work as we normally have to render first..
40327 if (this.footer && !this.footer.el && this.footer.xtype) {
40328 if (!this.wrapEl) {
40329 this.wrapEl = this.el.wrap();
40332 this.footer.container = this.wrapEl.createChild();
40334 this.footer = Roo.factory(this.footer, Roo);
40339 if(typeof config == "string"){
40340 this.title = config;
40342 Roo.apply(this, config);
40346 this.resizeEl = Roo.get(this.resizeEl, true);
40348 this.resizeEl = this.el;
40350 // handle view.xtype
40358 * Fires when this panel is activated.
40359 * @param {Roo.ContentPanel} this
40363 * @event deactivate
40364 * Fires when this panel is activated.
40365 * @param {Roo.ContentPanel} this
40367 "deactivate" : true,
40371 * Fires when this panel is resized if fitToFrame is true.
40372 * @param {Roo.ContentPanel} this
40373 * @param {Number} width The width after any component adjustments
40374 * @param {Number} height The height after any component adjustments
40380 * Fires when this tab is created
40381 * @param {Roo.ContentPanel} this
40387 * Fires when this content is scrolled
40388 * @param {Roo.ContentPanel} this
40389 * @param {Event} scrollEvent
40400 if(this.autoScroll && !this.iframe){
40401 this.resizeEl.setStyle("overflow", "auto");
40402 this.resizeEl.on('scroll', this.onScroll, this);
40404 // fix randome scrolling
40405 //this.el.on('scroll', function() {
40406 // Roo.log('fix random scolling');
40407 // this.scrollTo('top',0);
40410 content = content || this.content;
40412 this.setContent(content);
40414 if(config && config.url){
40415 this.setUrl(this.url, this.params, this.loadOnce);
40420 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40422 if (this.view && typeof(this.view.xtype) != 'undefined') {
40423 this.view.el = this.el.appendChild(document.createElement("div"));
40424 this.view = Roo.factory(this.view);
40425 this.view.render && this.view.render(false, '');
40429 this.fireEvent('render', this);
40432 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40442 /* Resize Element - use this to work out scroll etc. */
40445 setRegion : function(region){
40446 this.region = region;
40447 this.setActiveClass(region && !this.background);
40451 setActiveClass: function(state)
40454 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40455 this.el.setStyle('position','relative');
40457 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40458 this.el.setStyle('position', 'absolute');
40463 * Returns the toolbar for this Panel if one was configured.
40464 * @return {Roo.Toolbar}
40466 getToolbar : function(){
40467 return this.toolbar;
40470 setActiveState : function(active)
40472 this.active = active;
40473 this.setActiveClass(active);
40475 if(this.fireEvent("deactivate", this) === false){
40480 this.fireEvent("activate", this);
40484 * Updates this panel's element (not for iframe)
40485 * @param {String} content The new content
40486 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40488 setContent : function(content, loadScripts){
40493 this.el.update(content, loadScripts);
40496 ignoreResize : function(w, h){
40497 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40500 this.lastSize = {width: w, height: h};
40505 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40506 * @return {Roo.UpdateManager} The UpdateManager
40508 getUpdateManager : function(){
40512 return this.el.getUpdateManager();
40515 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40516 * Does not work with IFRAME contents
40517 * @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:
40520 url: "your-url.php",
40521 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40522 callback: yourFunction,
40523 scope: yourObject, //(optional scope)
40526 text: "Loading...",
40532 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40533 * 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.
40534 * @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}
40535 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40536 * @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.
40537 * @return {Roo.ContentPanel} this
40545 var um = this.el.getUpdateManager();
40546 um.update.apply(um, arguments);
40552 * 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.
40553 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40554 * @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)
40555 * @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)
40556 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40558 setUrl : function(url, params, loadOnce){
40560 this.iframeEl.dom.src = url;
40564 if(this.refreshDelegate){
40565 this.removeListener("activate", this.refreshDelegate);
40567 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40568 this.on("activate", this.refreshDelegate);
40569 return this.el.getUpdateManager();
40572 _handleRefresh : function(url, params, loadOnce){
40573 if(!loadOnce || !this.loaded){
40574 var updater = this.el.getUpdateManager();
40575 updater.update(url, params, this._setLoaded.createDelegate(this));
40579 _setLoaded : function(){
40580 this.loaded = true;
40584 * Returns this panel's id
40587 getId : function(){
40592 * Returns this panel's element - used by regiosn to add.
40593 * @return {Roo.Element}
40595 getEl : function(){
40596 return this.wrapEl || this.el;
40601 adjustForComponents : function(width, height)
40603 //Roo.log('adjustForComponents ');
40604 if(this.resizeEl != this.el){
40605 width -= this.el.getFrameWidth('lr');
40606 height -= this.el.getFrameWidth('tb');
40609 var te = this.toolbar.getEl();
40610 te.setWidth(width);
40611 height -= te.getHeight();
40614 var te = this.footer.getEl();
40615 te.setWidth(width);
40616 height -= te.getHeight();
40620 if(this.adjustments){
40621 width += this.adjustments[0];
40622 height += this.adjustments[1];
40624 return {"width": width, "height": height};
40627 setSize : function(width, height){
40628 if(this.fitToFrame && !this.ignoreResize(width, height)){
40629 if(this.fitContainer && this.resizeEl != this.el){
40630 this.el.setSize(width, height);
40632 var size = this.adjustForComponents(width, height);
40634 this.iframeEl.setSize(width,height);
40637 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40638 this.fireEvent('resize', this, size.width, size.height);
40645 * Returns this panel's title
40648 getTitle : function(){
40650 if (typeof(this.title) != 'object') {
40655 for (var k in this.title) {
40656 if (!this.title.hasOwnProperty(k)) {
40660 if (k.indexOf('-') >= 0) {
40661 var s = k.split('-');
40662 for (var i = 0; i<s.length; i++) {
40663 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40666 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40673 * Set this panel's title
40674 * @param {String} title
40676 setTitle : function(title){
40677 this.title = title;
40679 this.region.updatePanelTitle(this, title);
40684 * Returns true is this panel was configured to be closable
40685 * @return {Boolean}
40687 isClosable : function(){
40688 return this.closable;
40691 beforeSlide : function(){
40693 this.resizeEl.clip();
40696 afterSlide : function(){
40698 this.resizeEl.unclip();
40702 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40703 * Will fail silently if the {@link #setUrl} method has not been called.
40704 * This does not activate the panel, just updates its content.
40706 refresh : function(){
40707 if(this.refreshDelegate){
40708 this.loaded = false;
40709 this.refreshDelegate();
40714 * Destroys this panel
40716 destroy : function(){
40717 this.el.removeAllListeners();
40718 var tempEl = document.createElement("span");
40719 tempEl.appendChild(this.el.dom);
40720 tempEl.innerHTML = "";
40726 * form - if the content panel contains a form - this is a reference to it.
40727 * @type {Roo.form.Form}
40731 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40732 * This contains a reference to it.
40738 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40748 * @param {Object} cfg Xtype definition of item to add.
40752 getChildContainer: function () {
40753 return this.getEl();
40757 onScroll : function(e)
40759 this.fireEvent('scroll', this, e);
40764 var ret = new Roo.factory(cfg);
40769 if (cfg.xtype.match(/^Form$/)) {
40772 //if (this.footer) {
40773 // el = this.footer.container.insertSibling(false, 'before');
40775 el = this.el.createChild();
40778 this.form = new Roo.form.Form(cfg);
40781 if ( this.form.allItems.length) {
40782 this.form.render(el.dom);
40786 // should only have one of theses..
40787 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40788 // views.. should not be just added - used named prop 'view''
40790 cfg.el = this.el.appendChild(document.createElement("div"));
40793 var ret = new Roo.factory(cfg);
40795 ret.render && ret.render(false, ''); // render blank..
40805 * @class Roo.bootstrap.panel.Grid
40806 * @extends Roo.bootstrap.panel.Content
40808 * Create a new GridPanel.
40809 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40810 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40811 * @param {Object} config A the config object
40817 Roo.bootstrap.panel.Grid = function(config)
40821 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40822 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40824 config.el = this.wrapper;
40825 //this.el = this.wrapper;
40827 if (config.container) {
40828 // ctor'ed from a Border/panel.grid
40831 this.wrapper.setStyle("overflow", "hidden");
40832 this.wrapper.addClass('roo-grid-container');
40837 if(config.toolbar){
40838 var tool_el = this.wrapper.createChild();
40839 this.toolbar = Roo.factory(config.toolbar);
40841 if (config.toolbar.items) {
40842 ti = config.toolbar.items ;
40843 delete config.toolbar.items ;
40847 this.toolbar.render(tool_el);
40848 for(var i =0;i < ti.length;i++) {
40849 // Roo.log(['add child', items[i]]);
40850 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40852 this.toolbar.items = nitems;
40854 delete config.toolbar;
40857 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40858 config.grid.scrollBody = true;;
40859 config.grid.monitorWindowResize = false; // turn off autosizing
40860 config.grid.autoHeight = false;
40861 config.grid.autoWidth = false;
40863 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40865 if (config.background) {
40866 // render grid on panel activation (if panel background)
40867 this.on('activate', function(gp) {
40868 if (!gp.grid.rendered) {
40869 gp.grid.render(this.wrapper);
40870 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40875 this.grid.render(this.wrapper);
40876 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40879 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40880 // ??? needed ??? config.el = this.wrapper;
40885 // xtype created footer. - not sure if will work as we normally have to render first..
40886 if (this.footer && !this.footer.el && this.footer.xtype) {
40888 var ctr = this.grid.getView().getFooterPanel(true);
40889 this.footer.dataSource = this.grid.dataSource;
40890 this.footer = Roo.factory(this.footer, Roo);
40891 this.footer.render(ctr);
40901 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40902 getId : function(){
40903 return this.grid.id;
40907 * Returns the grid for this panel
40908 * @return {Roo.bootstrap.Table}
40910 getGrid : function(){
40914 setSize : function(width, height){
40915 if(!this.ignoreResize(width, height)){
40916 var grid = this.grid;
40917 var size = this.adjustForComponents(width, height);
40918 // tfoot is not a footer?
40921 var gridel = grid.getGridEl();
40922 gridel.setSize(size.width, size.height);
40924 var tbd = grid.getGridEl().select('tbody', true).first();
40925 var thd = grid.getGridEl().select('thead',true).first();
40926 var tbf= grid.getGridEl().select('tfoot', true).first();
40929 size.height -= tbf.getHeight();
40932 size.height -= thd.getHeight();
40935 tbd.setSize(size.width, size.height );
40936 // this is for the account management tab -seems to work there.
40937 var thd = grid.getGridEl().select('thead',true).first();
40939 // tbd.setSize(size.width, size.height - thd.getHeight());
40948 beforeSlide : function(){
40949 this.grid.getView().scroller.clip();
40952 afterSlide : function(){
40953 this.grid.getView().scroller.unclip();
40956 destroy : function(){
40957 this.grid.destroy();
40959 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40964 * @class Roo.bootstrap.panel.Nest
40965 * @extends Roo.bootstrap.panel.Content
40967 * Create a new Panel, that can contain a layout.Border.
40970 * @param {String/Object} config A string to set only the title or a config object
40972 Roo.bootstrap.panel.Nest = function(config)
40974 // construct with only one argument..
40975 /* FIXME - implement nicer consturctors
40976 if (layout.layout) {
40978 layout = config.layout;
40979 delete config.layout;
40981 if (layout.xtype && !layout.getEl) {
40982 // then layout needs constructing..
40983 layout = Roo.factory(layout, Roo);
40987 config.el = config.layout.getEl();
40989 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40991 config.layout.monitorWindowResize = false; // turn off autosizing
40992 this.layout = config.layout;
40993 this.layout.getEl().addClass("roo-layout-nested-layout");
40994 this.layout.parent = this;
41001 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41003 * @cfg {Roo.BorderLayout} layout The layout for this panel
41007 setSize : function(width, height){
41008 if(!this.ignoreResize(width, height)){
41009 var size = this.adjustForComponents(width, height);
41010 var el = this.layout.getEl();
41011 if (size.height < 1) {
41012 el.setWidth(size.width);
41014 el.setSize(size.width, size.height);
41016 var touch = el.dom.offsetWidth;
41017 this.layout.layout();
41018 // ie requires a double layout on the first pass
41019 if(Roo.isIE && !this.initialized){
41020 this.initialized = true;
41021 this.layout.layout();
41026 // activate all subpanels if not currently active..
41028 setActiveState : function(active){
41029 this.active = active;
41030 this.setActiveClass(active);
41033 this.fireEvent("deactivate", this);
41037 this.fireEvent("activate", this);
41038 // not sure if this should happen before or after..
41039 if (!this.layout) {
41040 return; // should not happen..
41043 for (var r in this.layout.regions) {
41044 reg = this.layout.getRegion(r);
41045 if (reg.getActivePanel()) {
41046 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41047 reg.setActivePanel(reg.getActivePanel());
41050 if (!reg.panels.length) {
41053 reg.showPanel(reg.getPanel(0));
41062 * Returns the nested BorderLayout for this panel
41063 * @return {Roo.BorderLayout}
41065 getLayout : function(){
41066 return this.layout;
41070 * Adds a xtype elements to the layout of the nested panel
41074 xtype : 'ContentPanel',
41081 xtype : 'NestedLayoutPanel',
41087 items : [ ... list of content panels or nested layout panels.. ]
41091 * @param {Object} cfg Xtype definition of item to add.
41093 addxtype : function(cfg) {
41094 return this.layout.addxtype(cfg);
41099 * Ext JS Library 1.1.1
41100 * Copyright(c) 2006-2007, Ext JS, LLC.
41102 * Originally Released Under LGPL - original licence link has changed is not relivant.
41105 * <script type="text/javascript">
41108 * @class Roo.TabPanel
41109 * @extends Roo.util.Observable
41110 * A lightweight tab container.
41114 // basic tabs 1, built from existing content
41115 var tabs = new Roo.TabPanel("tabs1");
41116 tabs.addTab("script", "View Script");
41117 tabs.addTab("markup", "View Markup");
41118 tabs.activate("script");
41120 // more advanced tabs, built from javascript
41121 var jtabs = new Roo.TabPanel("jtabs");
41122 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41124 // set up the UpdateManager
41125 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41126 var updater = tab2.getUpdateManager();
41127 updater.setDefaultUrl("ajax1.htm");
41128 tab2.on('activate', updater.refresh, updater, true);
41130 // Use setUrl for Ajax loading
41131 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41132 tab3.setUrl("ajax2.htm", null, true);
41135 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41138 jtabs.activate("jtabs-1");
41141 * Create a new TabPanel.
41142 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41143 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41145 Roo.bootstrap.panel.Tabs = function(config){
41147 * The container element for this TabPanel.
41148 * @type Roo.Element
41150 this.el = Roo.get(config.el);
41153 if(typeof config == "boolean"){
41154 this.tabPosition = config ? "bottom" : "top";
41156 Roo.apply(this, config);
41160 if(this.tabPosition == "bottom"){
41161 // if tabs are at the bottom = create the body first.
41162 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41163 this.el.addClass("roo-tabs-bottom");
41165 // next create the tabs holders
41167 if (this.tabPosition == "west"){
41169 var reg = this.region; // fake it..
41171 if (!reg.mgr.parent) {
41174 reg = reg.mgr.parent.region;
41176 Roo.log("got nest?");
41178 if (reg.mgr.getRegion('west')) {
41179 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41180 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41181 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41182 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41183 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41191 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41192 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41193 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41194 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41199 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41202 // finally - if tabs are at the top, then create the body last..
41203 if(this.tabPosition != "bottom"){
41204 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41205 * @type Roo.Element
41207 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41208 this.el.addClass("roo-tabs-top");
41212 this.bodyEl.setStyle("position", "relative");
41214 this.active = null;
41215 this.activateDelegate = this.activate.createDelegate(this);
41220 * Fires when the active tab changes
41221 * @param {Roo.TabPanel} this
41222 * @param {Roo.TabPanelItem} activePanel The new active tab
41226 * @event beforetabchange
41227 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41228 * @param {Roo.TabPanel} this
41229 * @param {Object} e Set cancel to true on this object to cancel the tab change
41230 * @param {Roo.TabPanelItem} tab The tab being changed to
41232 "beforetabchange" : true
41235 Roo.EventManager.onWindowResize(this.onResize, this);
41236 this.cpad = this.el.getPadding("lr");
41237 this.hiddenCount = 0;
41240 // toolbar on the tabbar support...
41241 if (this.toolbar) {
41242 alert("no toolbar support yet");
41243 this.toolbar = false;
41245 var tcfg = this.toolbar;
41246 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41247 this.toolbar = new Roo.Toolbar(tcfg);
41248 if (Roo.isSafari) {
41249 var tbl = tcfg.container.child('table', true);
41250 tbl.setAttribute('width', '100%');
41258 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41261 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41263 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41265 tabPosition : "top",
41267 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41269 currentTabWidth : 0,
41271 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41275 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41279 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41281 preferredTabWidth : 175,
41283 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41285 resizeTabs : false,
41287 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41289 monitorResize : true,
41291 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41293 toolbar : false, // set by caller..
41295 region : false, /// set by caller
41297 disableTooltips : true, // not used yet...
41300 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41301 * @param {String} id The id of the div to use <b>or create</b>
41302 * @param {String} text The text for the tab
41303 * @param {String} content (optional) Content to put in the TabPanelItem body
41304 * @param {Boolean} closable (optional) True to create a close icon on the tab
41305 * @return {Roo.TabPanelItem} The created TabPanelItem
41307 addTab : function(id, text, content, closable, tpl)
41309 var item = new Roo.bootstrap.panel.TabItem({
41313 closable : closable,
41316 this.addTabItem(item);
41318 item.setContent(content);
41324 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41325 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41326 * @return {Roo.TabPanelItem}
41328 getTab : function(id){
41329 return this.items[id];
41333 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41334 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41336 hideTab : function(id){
41337 var t = this.items[id];
41340 this.hiddenCount++;
41341 this.autoSizeTabs();
41346 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41347 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41349 unhideTab : function(id){
41350 var t = this.items[id];
41352 t.setHidden(false);
41353 this.hiddenCount--;
41354 this.autoSizeTabs();
41359 * Adds an existing {@link Roo.TabPanelItem}.
41360 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41362 addTabItem : function(item)
41364 this.items[item.id] = item;
41365 this.items.push(item);
41366 this.autoSizeTabs();
41367 // if(this.resizeTabs){
41368 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41369 // this.autoSizeTabs();
41371 // item.autoSize();
41376 * Removes a {@link Roo.TabPanelItem}.
41377 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41379 removeTab : function(id){
41380 var items = this.items;
41381 var tab = items[id];
41382 if(!tab) { return; }
41383 var index = items.indexOf(tab);
41384 if(this.active == tab && items.length > 1){
41385 var newTab = this.getNextAvailable(index);
41390 this.stripEl.dom.removeChild(tab.pnode.dom);
41391 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41392 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41394 items.splice(index, 1);
41395 delete this.items[tab.id];
41396 tab.fireEvent("close", tab);
41397 tab.purgeListeners();
41398 this.autoSizeTabs();
41401 getNextAvailable : function(start){
41402 var items = this.items;
41404 // look for a next tab that will slide over to
41405 // replace the one being removed
41406 while(index < items.length){
41407 var item = items[++index];
41408 if(item && !item.isHidden()){
41412 // if one isn't found select the previous tab (on the left)
41415 var item = items[--index];
41416 if(item && !item.isHidden()){
41424 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41425 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41427 disableTab : function(id){
41428 var tab = this.items[id];
41429 if(tab && this.active != tab){
41435 * Enables a {@link Roo.TabPanelItem} that is disabled.
41436 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41438 enableTab : function(id){
41439 var tab = this.items[id];
41444 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41445 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41446 * @return {Roo.TabPanelItem} The TabPanelItem.
41448 activate : function(id)
41450 //Roo.log('activite:' + id);
41452 var tab = this.items[id];
41456 if(tab == this.active || tab.disabled){
41460 this.fireEvent("beforetabchange", this, e, tab);
41461 if(e.cancel !== true && !tab.disabled){
41463 this.active.hide();
41465 this.active = this.items[id];
41466 this.active.show();
41467 this.fireEvent("tabchange", this, this.active);
41473 * Gets the active {@link Roo.TabPanelItem}.
41474 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41476 getActiveTab : function(){
41477 return this.active;
41481 * Updates the tab body element to fit the height of the container element
41482 * for overflow scrolling
41483 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41485 syncHeight : function(targetHeight){
41486 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41487 var bm = this.bodyEl.getMargins();
41488 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41489 this.bodyEl.setHeight(newHeight);
41493 onResize : function(){
41494 if(this.monitorResize){
41495 this.autoSizeTabs();
41500 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41502 beginUpdate : function(){
41503 this.updating = true;
41507 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41509 endUpdate : function(){
41510 this.updating = false;
41511 this.autoSizeTabs();
41515 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41517 autoSizeTabs : function()
41519 var count = this.items.length;
41520 var vcount = count - this.hiddenCount;
41523 this.stripEl.hide();
41525 this.stripEl.show();
41528 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41533 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41534 var availWidth = Math.floor(w / vcount);
41535 var b = this.stripBody;
41536 if(b.getWidth() > w){
41537 var tabs = this.items;
41538 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41539 if(availWidth < this.minTabWidth){
41540 /*if(!this.sleft){ // incomplete scrolling code
41541 this.createScrollButtons();
41544 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41547 if(this.currentTabWidth < this.preferredTabWidth){
41548 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41554 * Returns the number of tabs in this TabPanel.
41557 getCount : function(){
41558 return this.items.length;
41562 * Resizes all the tabs to the passed width
41563 * @param {Number} The new width
41565 setTabWidth : function(width){
41566 this.currentTabWidth = width;
41567 for(var i = 0, len = this.items.length; i < len; i++) {
41568 if(!this.items[i].isHidden()) {
41569 this.items[i].setWidth(width);
41575 * Destroys this TabPanel
41576 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41578 destroy : function(removeEl){
41579 Roo.EventManager.removeResizeListener(this.onResize, this);
41580 for(var i = 0, len = this.items.length; i < len; i++){
41581 this.items[i].purgeListeners();
41583 if(removeEl === true){
41584 this.el.update("");
41589 createStrip : function(container)
41591 var strip = document.createElement("nav");
41592 strip.className = Roo.bootstrap.version == 4 ?
41593 "navbar-light bg-light" :
41594 "navbar navbar-default"; //"x-tabs-wrap";
41595 container.appendChild(strip);
41599 createStripList : function(strip)
41601 // div wrapper for retard IE
41602 // returns the "tr" element.
41603 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41604 //'<div class="x-tabs-strip-wrap">'+
41605 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41606 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41607 return strip.firstChild; //.firstChild.firstChild.firstChild;
41609 createBody : function(container)
41611 var body = document.createElement("div");
41612 Roo.id(body, "tab-body");
41613 //Roo.fly(body).addClass("x-tabs-body");
41614 Roo.fly(body).addClass("tab-content");
41615 container.appendChild(body);
41618 createItemBody :function(bodyEl, id){
41619 var body = Roo.getDom(id);
41621 body = document.createElement("div");
41624 //Roo.fly(body).addClass("x-tabs-item-body");
41625 Roo.fly(body).addClass("tab-pane");
41626 bodyEl.insertBefore(body, bodyEl.firstChild);
41630 createStripElements : function(stripEl, text, closable, tpl)
41632 var td = document.createElement("li"); // was td..
41633 td.className = 'nav-item';
41635 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41638 stripEl.appendChild(td);
41640 td.className = "x-tabs-closable";
41641 if(!this.closeTpl){
41642 this.closeTpl = new Roo.Template(
41643 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41644 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41645 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41648 var el = this.closeTpl.overwrite(td, {"text": text});
41649 var close = el.getElementsByTagName("div")[0];
41650 var inner = el.getElementsByTagName("em")[0];
41651 return {"el": el, "close": close, "inner": inner};
41654 // not sure what this is..
41655 // if(!this.tabTpl){
41656 //this.tabTpl = new Roo.Template(
41657 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41658 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41660 // this.tabTpl = new Roo.Template(
41661 // '<a href="#">' +
41662 // '<span unselectable="on"' +
41663 // (this.disableTooltips ? '' : ' title="{text}"') +
41664 // ' >{text}</span></a>'
41670 var template = tpl || this.tabTpl || false;
41673 template = new Roo.Template(
41674 Roo.bootstrap.version == 4 ?
41676 '<a class="nav-link" href="#" unselectable="on"' +
41677 (this.disableTooltips ? '' : ' title="{text}"') +
41680 '<a class="nav-link" href="#">' +
41681 '<span unselectable="on"' +
41682 (this.disableTooltips ? '' : ' title="{text}"') +
41683 ' >{text}</span></a>'
41688 switch (typeof(template)) {
41692 template = new Roo.Template(template);
41698 var el = template.overwrite(td, {"text": text});
41700 var inner = el.getElementsByTagName("span")[0];
41702 return {"el": el, "inner": inner};
41710 * @class Roo.TabPanelItem
41711 * @extends Roo.util.Observable
41712 * Represents an individual item (tab plus body) in a TabPanel.
41713 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41714 * @param {String} id The id of this TabPanelItem
41715 * @param {String} text The text for the tab of this TabPanelItem
41716 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41718 Roo.bootstrap.panel.TabItem = function(config){
41720 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41721 * @type Roo.TabPanel
41723 this.tabPanel = config.panel;
41725 * The id for this TabPanelItem
41728 this.id = config.id;
41730 this.disabled = false;
41732 this.text = config.text;
41734 this.loaded = false;
41735 this.closable = config.closable;
41738 * The body element for this TabPanelItem.
41739 * @type Roo.Element
41741 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41742 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41743 this.bodyEl.setStyle("display", "block");
41744 this.bodyEl.setStyle("zoom", "1");
41745 //this.hideAction();
41747 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41749 this.el = Roo.get(els.el);
41750 this.inner = Roo.get(els.inner, true);
41751 this.textEl = Roo.bootstrap.version == 4 ?
41752 this.el : Roo.get(this.el.dom.firstChild, true);
41754 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41755 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41758 // this.el.on("mousedown", this.onTabMouseDown, this);
41759 this.el.on("click", this.onTabClick, this);
41761 if(config.closable){
41762 var c = Roo.get(els.close, true);
41763 c.dom.title = this.closeText;
41764 c.addClassOnOver("close-over");
41765 c.on("click", this.closeClick, this);
41771 * Fires when this tab becomes the active tab.
41772 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41773 * @param {Roo.TabPanelItem} this
41777 * @event beforeclose
41778 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41779 * @param {Roo.TabPanelItem} this
41780 * @param {Object} e Set cancel to true on this object to cancel the close.
41782 "beforeclose": true,
41785 * Fires when this tab is closed.
41786 * @param {Roo.TabPanelItem} this
41790 * @event deactivate
41791 * Fires when this tab is no longer the active tab.
41792 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41793 * @param {Roo.TabPanelItem} this
41795 "deactivate" : true
41797 this.hidden = false;
41799 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41802 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41804 purgeListeners : function(){
41805 Roo.util.Observable.prototype.purgeListeners.call(this);
41806 this.el.removeAllListeners();
41809 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41812 this.status_node.addClass("active");
41815 this.tabPanel.stripWrap.repaint();
41817 this.fireEvent("activate", this.tabPanel, this);
41821 * Returns true if this tab is the active tab.
41822 * @return {Boolean}
41824 isActive : function(){
41825 return this.tabPanel.getActiveTab() == this;
41829 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41832 this.status_node.removeClass("active");
41834 this.fireEvent("deactivate", this.tabPanel, this);
41837 hideAction : function(){
41838 this.bodyEl.hide();
41839 this.bodyEl.setStyle("position", "absolute");
41840 this.bodyEl.setLeft("-20000px");
41841 this.bodyEl.setTop("-20000px");
41844 showAction : function(){
41845 this.bodyEl.setStyle("position", "relative");
41846 this.bodyEl.setTop("");
41847 this.bodyEl.setLeft("");
41848 this.bodyEl.show();
41852 * Set the tooltip for the tab.
41853 * @param {String} tooltip The tab's tooltip
41855 setTooltip : function(text){
41856 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41857 this.textEl.dom.qtip = text;
41858 this.textEl.dom.removeAttribute('title');
41860 this.textEl.dom.title = text;
41864 onTabClick : function(e){
41865 e.preventDefault();
41866 this.tabPanel.activate(this.id);
41869 onTabMouseDown : function(e){
41870 e.preventDefault();
41871 this.tabPanel.activate(this.id);
41874 getWidth : function(){
41875 return this.inner.getWidth();
41878 setWidth : function(width){
41879 var iwidth = width - this.linode.getPadding("lr");
41880 this.inner.setWidth(iwidth);
41881 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41882 this.linode.setWidth(width);
41886 * Show or hide the tab
41887 * @param {Boolean} hidden True to hide or false to show.
41889 setHidden : function(hidden){
41890 this.hidden = hidden;
41891 this.linode.setStyle("display", hidden ? "none" : "");
41895 * Returns true if this tab is "hidden"
41896 * @return {Boolean}
41898 isHidden : function(){
41899 return this.hidden;
41903 * Returns the text for this tab
41906 getText : function(){
41910 autoSize : function(){
41911 //this.el.beginMeasure();
41912 this.textEl.setWidth(1);
41914 * #2804 [new] Tabs in Roojs
41915 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41917 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41918 //this.el.endMeasure();
41922 * Sets the text for the tab (Note: this also sets the tooltip text)
41923 * @param {String} text The tab's text and tooltip
41925 setText : function(text){
41927 this.textEl.update(text);
41928 this.setTooltip(text);
41929 //if(!this.tabPanel.resizeTabs){
41930 // this.autoSize();
41934 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41936 activate : function(){
41937 this.tabPanel.activate(this.id);
41941 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41943 disable : function(){
41944 if(this.tabPanel.active != this){
41945 this.disabled = true;
41946 this.status_node.addClass("disabled");
41951 * Enables this TabPanelItem if it was previously disabled.
41953 enable : function(){
41954 this.disabled = false;
41955 this.status_node.removeClass("disabled");
41959 * Sets the content for this TabPanelItem.
41960 * @param {String} content The content
41961 * @param {Boolean} loadScripts true to look for and load scripts
41963 setContent : function(content, loadScripts){
41964 this.bodyEl.update(content, loadScripts);
41968 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41969 * @return {Roo.UpdateManager} The UpdateManager
41971 getUpdateManager : function(){
41972 return this.bodyEl.getUpdateManager();
41976 * Set a URL to be used to load the content for this TabPanelItem.
41977 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41978 * @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)
41979 * @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)
41980 * @return {Roo.UpdateManager} The UpdateManager
41982 setUrl : function(url, params, loadOnce){
41983 if(this.refreshDelegate){
41984 this.un('activate', this.refreshDelegate);
41986 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41987 this.on("activate", this.refreshDelegate);
41988 return this.bodyEl.getUpdateManager();
41992 _handleRefresh : function(url, params, loadOnce){
41993 if(!loadOnce || !this.loaded){
41994 var updater = this.bodyEl.getUpdateManager();
41995 updater.update(url, params, this._setLoaded.createDelegate(this));
42000 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42001 * Will fail silently if the setUrl method has not been called.
42002 * This does not activate the panel, just updates its content.
42004 refresh : function(){
42005 if(this.refreshDelegate){
42006 this.loaded = false;
42007 this.refreshDelegate();
42012 _setLoaded : function(){
42013 this.loaded = true;
42017 closeClick : function(e){
42020 this.fireEvent("beforeclose", this, o);
42021 if(o.cancel !== true){
42022 this.tabPanel.removeTab(this.id);
42026 * The text displayed in the tooltip for the close icon.
42029 closeText : "Close this tab"
42032 * This script refer to:
42033 * Title: International Telephone Input
42034 * Author: Jack O'Connor
42035 * Code version: v12.1.12
42036 * Availability: https://github.com/jackocnr/intl-tel-input.git
42039 Roo.bootstrap.PhoneInputData = function() {
42042 "Afghanistan (افغانستان)",
42047 "Albania (Shqipëri)",
42052 "Algeria (الجزائر)",
42077 "Antigua and Barbuda",
42087 "Armenia (Հայաստան)",
42103 "Austria (Österreich)",
42108 "Azerbaijan (Azərbaycan)",
42118 "Bahrain (البحرين)",
42123 "Bangladesh (বাংলাদেশ)",
42133 "Belarus (Беларусь)",
42138 "Belgium (België)",
42168 "Bosnia and Herzegovina (Босна и Херцеговина)",
42183 "British Indian Ocean Territory",
42188 "British Virgin Islands",
42198 "Bulgaria (България)",
42208 "Burundi (Uburundi)",
42213 "Cambodia (កម្ពុជា)",
42218 "Cameroon (Cameroun)",
42227 ["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"]
42230 "Cape Verde (Kabu Verdi)",
42235 "Caribbean Netherlands",
42246 "Central African Republic (République centrafricaine)",
42266 "Christmas Island",
42272 "Cocos (Keeling) Islands",
42283 "Comoros (جزر القمر)",
42288 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42293 "Congo (Republic) (Congo-Brazzaville)",
42313 "Croatia (Hrvatska)",
42334 "Czech Republic (Česká republika)",
42339 "Denmark (Danmark)",
42354 "Dominican Republic (República Dominicana)",
42358 ["809", "829", "849"]
42376 "Equatorial Guinea (Guinea Ecuatorial)",
42396 "Falkland Islands (Islas Malvinas)",
42401 "Faroe Islands (Føroyar)",
42422 "French Guiana (Guyane française)",
42427 "French Polynesia (Polynésie française)",
42442 "Georgia (საქართველო)",
42447 "Germany (Deutschland)",
42467 "Greenland (Kalaallit Nunaat)",
42504 "Guinea-Bissau (Guiné Bissau)",
42529 "Hungary (Magyarország)",
42534 "Iceland (Ísland)",
42554 "Iraq (العراق)",
42570 "Israel (ישראל)",
42597 "Jordan (الأردن)",
42602 "Kazakhstan (Казахстан)",
42623 "Kuwait (الكويت)",
42628 "Kyrgyzstan (Кыргызстан)",
42638 "Latvia (Latvija)",
42643 "Lebanon (لبنان)",
42658 "Libya (ليبيا)",
42668 "Lithuania (Lietuva)",
42683 "Macedonia (FYROM) (Македонија)",
42688 "Madagascar (Madagasikara)",
42718 "Marshall Islands",
42728 "Mauritania (موريتانيا)",
42733 "Mauritius (Moris)",
42754 "Moldova (Republica Moldova)",
42764 "Mongolia (Монгол)",
42769 "Montenegro (Crna Gora)",
42779 "Morocco (المغرب)",
42785 "Mozambique (Moçambique)",
42790 "Myanmar (Burma) (မြန်မာ)",
42795 "Namibia (Namibië)",
42810 "Netherlands (Nederland)",
42815 "New Caledonia (Nouvelle-Calédonie)",
42850 "North Korea (조선 민주주의 인민 공화국)",
42855 "Northern Mariana Islands",
42871 "Pakistan (پاکستان)",
42881 "Palestine (فلسطين)",
42891 "Papua New Guinea",
42933 "Réunion (La Réunion)",
42939 "Romania (România)",
42955 "Saint Barthélemy",
42966 "Saint Kitts and Nevis",
42976 "Saint Martin (Saint-Martin (partie française))",
42982 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42987 "Saint Vincent and the Grenadines",
43002 "São Tomé and Príncipe (São Tomé e Príncipe)",
43007 "Saudi Arabia (المملكة العربية السعودية)",
43012 "Senegal (Sénégal)",
43042 "Slovakia (Slovensko)",
43047 "Slovenia (Slovenija)",
43057 "Somalia (Soomaaliya)",
43067 "South Korea (대한민국)",
43072 "South Sudan (جنوب السودان)",
43082 "Sri Lanka (ශ්රී ලංකාව)",
43087 "Sudan (السودان)",
43097 "Svalbard and Jan Mayen",
43108 "Sweden (Sverige)",
43113 "Switzerland (Schweiz)",
43118 "Syria (سوريا)",
43163 "Trinidad and Tobago",
43168 "Tunisia (تونس)",
43173 "Turkey (Türkiye)",
43183 "Turks and Caicos Islands",
43193 "U.S. Virgin Islands",
43203 "Ukraine (Україна)",
43208 "United Arab Emirates (الإمارات العربية المتحدة)",
43230 "Uzbekistan (Oʻzbekiston)",
43240 "Vatican City (Città del Vaticano)",
43251 "Vietnam (Việt Nam)",
43256 "Wallis and Futuna (Wallis-et-Futuna)",
43261 "Western Sahara (الصحراء الغربية)",
43267 "Yemen (اليمن)",
43291 * This script refer to:
43292 * Title: International Telephone Input
43293 * Author: Jack O'Connor
43294 * Code version: v12.1.12
43295 * Availability: https://github.com/jackocnr/intl-tel-input.git
43299 * @class Roo.bootstrap.PhoneInput
43300 * @extends Roo.bootstrap.TriggerField
43301 * An input with International dial-code selection
43303 * @cfg {String} defaultDialCode default '+852'
43304 * @cfg {Array} preferedCountries default []
43307 * Create a new PhoneInput.
43308 * @param {Object} config Configuration options
43311 Roo.bootstrap.PhoneInput = function(config) {
43312 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43315 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43317 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43319 listWidth: undefined,
43321 selectedClass: 'active',
43323 invalidClass : "has-warning",
43325 validClass: 'has-success',
43327 allowed: '0123456789',
43332 * @cfg {String} defaultDialCode The default dial code when initializing the input
43334 defaultDialCode: '+852',
43337 * @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
43339 preferedCountries: false,
43341 getAutoCreate : function()
43343 var data = Roo.bootstrap.PhoneInputData();
43344 var align = this.labelAlign || this.parentLabelAlign();
43347 this.allCountries = [];
43348 this.dialCodeMapping = [];
43350 for (var i = 0; i < data.length; i++) {
43352 this.allCountries[i] = {
43356 priority: c[3] || 0,
43357 areaCodes: c[4] || null
43359 this.dialCodeMapping[c[2]] = {
43362 priority: c[3] || 0,
43363 areaCodes: c[4] || null
43375 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43376 maxlength: this.max_length,
43377 cls : 'form-control tel-input',
43378 autocomplete: 'new-password'
43381 var hiddenInput = {
43384 cls: 'hidden-tel-input'
43388 hiddenInput.name = this.name;
43391 if (this.disabled) {
43392 input.disabled = true;
43395 var flag_container = {
43412 cls: this.hasFeedback ? 'has-feedback' : '',
43418 cls: 'dial-code-holder',
43425 cls: 'roo-select2-container input-group',
43432 if (this.fieldLabel.length) {
43435 tooltip: 'This field is required'
43441 cls: 'control-label',
43447 html: this.fieldLabel
43450 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43456 if(this.indicatorpos == 'right') {
43457 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43464 if(align == 'left') {
43472 if(this.labelWidth > 12){
43473 label.style = "width: " + this.labelWidth + 'px';
43475 if(this.labelWidth < 13 && this.labelmd == 0){
43476 this.labelmd = this.labelWidth;
43478 if(this.labellg > 0){
43479 label.cls += ' col-lg-' + this.labellg;
43480 input.cls += ' col-lg-' + (12 - this.labellg);
43482 if(this.labelmd > 0){
43483 label.cls += ' col-md-' + this.labelmd;
43484 container.cls += ' col-md-' + (12 - this.labelmd);
43486 if(this.labelsm > 0){
43487 label.cls += ' col-sm-' + this.labelsm;
43488 container.cls += ' col-sm-' + (12 - this.labelsm);
43490 if(this.labelxs > 0){
43491 label.cls += ' col-xs-' + this.labelxs;
43492 container.cls += ' col-xs-' + (12 - this.labelxs);
43502 var settings = this;
43504 ['xs','sm','md','lg'].map(function(size){
43505 if (settings[size]) {
43506 cfg.cls += ' col-' + size + '-' + settings[size];
43510 this.store = new Roo.data.Store({
43511 proxy : new Roo.data.MemoryProxy({}),
43512 reader : new Roo.data.JsonReader({
43523 'name' : 'dialCode',
43527 'name' : 'priority',
43531 'name' : 'areaCodes',
43538 if(!this.preferedCountries) {
43539 this.preferedCountries = [
43546 var p = this.preferedCountries.reverse();
43549 for (var i = 0; i < p.length; i++) {
43550 for (var j = 0; j < this.allCountries.length; j++) {
43551 if(this.allCountries[j].iso2 == p[i]) {
43552 var t = this.allCountries[j];
43553 this.allCountries.splice(j,1);
43554 this.allCountries.unshift(t);
43560 this.store.proxy.data = {
43562 data: this.allCountries
43568 initEvents : function()
43571 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43573 this.indicator = this.indicatorEl();
43574 this.flag = this.flagEl();
43575 this.dialCodeHolder = this.dialCodeHolderEl();
43577 this.trigger = this.el.select('div.flag-box',true).first();
43578 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43583 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43584 _this.list.setWidth(lw);
43587 this.list.on('mouseover', this.onViewOver, this);
43588 this.list.on('mousemove', this.onViewMove, this);
43589 this.inputEl().on("keyup", this.onKeyUp, this);
43590 this.inputEl().on("keypress", this.onKeyPress, this);
43592 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43594 this.view = new Roo.View(this.list, this.tpl, {
43595 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43598 this.view.on('click', this.onViewClick, this);
43599 this.setValue(this.defaultDialCode);
43602 onTriggerClick : function(e)
43604 Roo.log('trigger click');
43609 if(this.isExpanded()){
43611 this.hasFocus = false;
43613 this.store.load({});
43614 this.hasFocus = true;
43619 isExpanded : function()
43621 return this.list.isVisible();
43624 collapse : function()
43626 if(!this.isExpanded()){
43630 Roo.get(document).un('mousedown', this.collapseIf, this);
43631 Roo.get(document).un('mousewheel', this.collapseIf, this);
43632 this.fireEvent('collapse', this);
43636 expand : function()
43640 if(this.isExpanded() || !this.hasFocus){
43644 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43645 this.list.setWidth(lw);
43648 this.restrictHeight();
43650 Roo.get(document).on('mousedown', this.collapseIf, this);
43651 Roo.get(document).on('mousewheel', this.collapseIf, this);
43653 this.fireEvent('expand', this);
43656 restrictHeight : function()
43658 this.list.alignTo(this.inputEl(), this.listAlign);
43659 this.list.alignTo(this.inputEl(), this.listAlign);
43662 onViewOver : function(e, t)
43664 if(this.inKeyMode){
43667 var item = this.view.findItemFromChild(t);
43670 var index = this.view.indexOf(item);
43671 this.select(index, false);
43676 onViewClick : function(view, doFocus, el, e)
43678 var index = this.view.getSelectedIndexes()[0];
43680 var r = this.store.getAt(index);
43683 this.onSelect(r, index);
43685 if(doFocus !== false && !this.blockFocus){
43686 this.inputEl().focus();
43690 onViewMove : function(e, t)
43692 this.inKeyMode = false;
43695 select : function(index, scrollIntoView)
43697 this.selectedIndex = index;
43698 this.view.select(index);
43699 if(scrollIntoView !== false){
43700 var el = this.view.getNode(index);
43702 this.list.scrollChildIntoView(el, false);
43707 createList : function()
43709 this.list = Roo.get(document.body).createChild({
43711 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43712 style: 'display:none'
43715 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43718 collapseIf : function(e)
43720 var in_combo = e.within(this.el);
43721 var in_list = e.within(this.list);
43722 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43724 if (in_combo || in_list || is_list) {
43730 onSelect : function(record, index)
43732 if(this.fireEvent('beforeselect', this, record, index) !== false){
43734 this.setFlagClass(record.data.iso2);
43735 this.setDialCode(record.data.dialCode);
43736 this.hasFocus = false;
43738 this.fireEvent('select', this, record, index);
43742 flagEl : function()
43744 var flag = this.el.select('div.flag',true).first();
43751 dialCodeHolderEl : function()
43753 var d = this.el.select('input.dial-code-holder',true).first();
43760 setDialCode : function(v)
43762 this.dialCodeHolder.dom.value = '+'+v;
43765 setFlagClass : function(n)
43767 this.flag.dom.className = 'flag '+n;
43770 getValue : function()
43772 var v = this.inputEl().getValue();
43773 if(this.dialCodeHolder) {
43774 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43779 setValue : function(v)
43781 var d = this.getDialCode(v);
43783 //invalid dial code
43784 if(v.length == 0 || !d || d.length == 0) {
43786 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43787 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43793 this.setFlagClass(this.dialCodeMapping[d].iso2);
43794 this.setDialCode(d);
43795 this.inputEl().dom.value = v.replace('+'+d,'');
43796 this.hiddenEl().dom.value = this.getValue();
43801 getDialCode : function(v)
43805 if (v.length == 0) {
43806 return this.dialCodeHolder.dom.value;
43810 if (v.charAt(0) != "+") {
43813 var numericChars = "";
43814 for (var i = 1; i < v.length; i++) {
43815 var c = v.charAt(i);
43818 if (this.dialCodeMapping[numericChars]) {
43819 dialCode = v.substr(1, i);
43821 if (numericChars.length == 4) {
43831 this.setValue(this.defaultDialCode);
43835 hiddenEl : function()
43837 return this.el.select('input.hidden-tel-input',true).first();
43840 // after setting val
43841 onKeyUp : function(e){
43842 this.setValue(this.getValue());
43845 onKeyPress : function(e){
43846 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43853 * @class Roo.bootstrap.MoneyField
43854 * @extends Roo.bootstrap.ComboBox
43855 * Bootstrap MoneyField class
43858 * Create a new MoneyField.
43859 * @param {Object} config Configuration options
43862 Roo.bootstrap.MoneyField = function(config) {
43864 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43868 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43871 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43873 allowDecimals : true,
43875 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43877 decimalSeparator : ".",
43879 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43881 decimalPrecision : 0,
43883 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43885 allowNegative : true,
43887 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43891 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43893 minValue : Number.NEGATIVE_INFINITY,
43895 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43897 maxValue : Number.MAX_VALUE,
43899 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43901 minText : "The minimum value for this field is {0}",
43903 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43905 maxText : "The maximum value for this field is {0}",
43907 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43908 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43910 nanText : "{0} is not a valid number",
43912 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43916 * @cfg {String} defaults currency of the MoneyField
43917 * value should be in lkey
43919 defaultCurrency : false,
43921 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43923 thousandsDelimiter : false,
43925 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43934 * @cfg {Roo.data.Store} store Store to lookup currency??
43938 getAutoCreate : function()
43940 var align = this.labelAlign || this.parentLabelAlign();
43952 cls : 'form-control roo-money-amount-input',
43953 autocomplete: 'new-password'
43956 var hiddenInput = {
43960 cls: 'hidden-number-input'
43963 if(this.max_length) {
43964 input.maxlength = this.max_length;
43968 hiddenInput.name = this.name;
43971 if (this.disabled) {
43972 input.disabled = true;
43975 var clg = 12 - this.inputlg;
43976 var cmd = 12 - this.inputmd;
43977 var csm = 12 - this.inputsm;
43978 var cxs = 12 - this.inputxs;
43982 cls : 'row roo-money-field',
43986 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43990 cls: 'roo-select2-container input-group',
43994 cls : 'form-control roo-money-currency-input',
43995 autocomplete: 'new-password',
43997 name : this.currencyName
44001 cls : 'input-group-addon',
44015 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44019 cls: this.hasFeedback ? 'has-feedback' : '',
44030 if (this.fieldLabel.length) {
44033 tooltip: 'This field is required'
44039 cls: 'control-label',
44045 html: this.fieldLabel
44048 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44054 if(this.indicatorpos == 'right') {
44055 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44062 if(align == 'left') {
44070 if(this.labelWidth > 12){
44071 label.style = "width: " + this.labelWidth + 'px';
44073 if(this.labelWidth < 13 && this.labelmd == 0){
44074 this.labelmd = this.labelWidth;
44076 if(this.labellg > 0){
44077 label.cls += ' col-lg-' + this.labellg;
44078 input.cls += ' col-lg-' + (12 - this.labellg);
44080 if(this.labelmd > 0){
44081 label.cls += ' col-md-' + this.labelmd;
44082 container.cls += ' col-md-' + (12 - this.labelmd);
44084 if(this.labelsm > 0){
44085 label.cls += ' col-sm-' + this.labelsm;
44086 container.cls += ' col-sm-' + (12 - this.labelsm);
44088 if(this.labelxs > 0){
44089 label.cls += ' col-xs-' + this.labelxs;
44090 container.cls += ' col-xs-' + (12 - this.labelxs);
44101 var settings = this;
44103 ['xs','sm','md','lg'].map(function(size){
44104 if (settings[size]) {
44105 cfg.cls += ' col-' + size + '-' + settings[size];
44112 initEvents : function()
44114 this.indicator = this.indicatorEl();
44116 this.initCurrencyEvent();
44118 this.initNumberEvent();
44121 initCurrencyEvent : function()
44124 throw "can not find store for combo";
44127 this.store = Roo.factory(this.store, Roo.data);
44128 this.store.parent = this;
44132 this.triggerEl = this.el.select('.input-group-addon', true).first();
44134 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44139 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44140 _this.list.setWidth(lw);
44143 this.list.on('mouseover', this.onViewOver, this);
44144 this.list.on('mousemove', this.onViewMove, this);
44145 this.list.on('scroll', this.onViewScroll, this);
44148 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44151 this.view = new Roo.View(this.list, this.tpl, {
44152 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44155 this.view.on('click', this.onViewClick, this);
44157 this.store.on('beforeload', this.onBeforeLoad, this);
44158 this.store.on('load', this.onLoad, this);
44159 this.store.on('loadexception', this.onLoadException, this);
44161 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44162 "up" : function(e){
44163 this.inKeyMode = true;
44167 "down" : function(e){
44168 if(!this.isExpanded()){
44169 this.onTriggerClick();
44171 this.inKeyMode = true;
44176 "enter" : function(e){
44179 if(this.fireEvent("specialkey", this, e)){
44180 this.onViewClick(false);
44186 "esc" : function(e){
44190 "tab" : function(e){
44193 if(this.fireEvent("specialkey", this, e)){
44194 this.onViewClick(false);
44202 doRelay : function(foo, bar, hname){
44203 if(hname == 'down' || this.scope.isExpanded()){
44204 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44212 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44216 initNumberEvent : function(e)
44218 this.inputEl().on("keydown" , this.fireKey, this);
44219 this.inputEl().on("focus", this.onFocus, this);
44220 this.inputEl().on("blur", this.onBlur, this);
44222 this.inputEl().relayEvent('keyup', this);
44224 if(this.indicator){
44225 this.indicator.addClass('invisible');
44228 this.originalValue = this.getValue();
44230 if(this.validationEvent == 'keyup'){
44231 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44232 this.inputEl().on('keyup', this.filterValidation, this);
44234 else if(this.validationEvent !== false){
44235 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44238 if(this.selectOnFocus){
44239 this.on("focus", this.preFocus, this);
44242 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44243 this.inputEl().on("keypress", this.filterKeys, this);
44245 this.inputEl().relayEvent('keypress', this);
44248 var allowed = "0123456789";
44250 if(this.allowDecimals){
44251 allowed += this.decimalSeparator;
44254 if(this.allowNegative){
44258 if(this.thousandsDelimiter) {
44262 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44264 var keyPress = function(e){
44266 var k = e.getKey();
44268 var c = e.getCharCode();
44271 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44272 allowed.indexOf(String.fromCharCode(c)) === -1
44278 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44282 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44287 this.inputEl().on("keypress", keyPress, this);
44291 onTriggerClick : function(e)
44298 this.loadNext = false;
44300 if(this.isExpanded()){
44305 this.hasFocus = true;
44307 if(this.triggerAction == 'all') {
44308 this.doQuery(this.allQuery, true);
44312 this.doQuery(this.getRawValue());
44315 getCurrency : function()
44317 var v = this.currencyEl().getValue();
44322 restrictHeight : function()
44324 this.list.alignTo(this.currencyEl(), this.listAlign);
44325 this.list.alignTo(this.currencyEl(), this.listAlign);
44328 onViewClick : function(view, doFocus, el, e)
44330 var index = this.view.getSelectedIndexes()[0];
44332 var r = this.store.getAt(index);
44335 this.onSelect(r, index);
44339 onSelect : function(record, index){
44341 if(this.fireEvent('beforeselect', this, record, index) !== false){
44343 this.setFromCurrencyData(index > -1 ? record.data : false);
44347 this.fireEvent('select', this, record, index);
44351 setFromCurrencyData : function(o)
44355 this.lastCurrency = o;
44357 if (this.currencyField) {
44358 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44360 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44363 this.lastSelectionText = currency;
44365 //setting default currency
44366 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44367 this.setCurrency(this.defaultCurrency);
44371 this.setCurrency(currency);
44374 setFromData : function(o)
44378 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44380 this.setFromCurrencyData(c);
44385 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44387 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44390 this.setValue(value);
44394 setCurrency : function(v)
44396 this.currencyValue = v;
44399 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44404 setValue : function(v)
44406 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44412 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44414 this.inputEl().dom.value = (v == '') ? '' :
44415 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44417 if(!this.allowZero && v === '0') {
44418 this.hiddenEl().dom.value = '';
44419 this.inputEl().dom.value = '';
44426 getRawValue : function()
44428 var v = this.inputEl().getValue();
44433 getValue : function()
44435 return this.fixPrecision(this.parseValue(this.getRawValue()));
44438 parseValue : function(value)
44440 if(this.thousandsDelimiter) {
44442 r = new RegExp(",", "g");
44443 value = value.replace(r, "");
44446 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44447 return isNaN(value) ? '' : value;
44451 fixPrecision : function(value)
44453 if(this.thousandsDelimiter) {
44455 r = new RegExp(",", "g");
44456 value = value.replace(r, "");
44459 var nan = isNaN(value);
44461 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44462 return nan ? '' : value;
44464 return parseFloat(value).toFixed(this.decimalPrecision);
44467 decimalPrecisionFcn : function(v)
44469 return Math.floor(v);
44472 validateValue : function(value)
44474 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44478 var num = this.parseValue(value);
44481 this.markInvalid(String.format(this.nanText, value));
44485 if(num < this.minValue){
44486 this.markInvalid(String.format(this.minText, this.minValue));
44490 if(num > this.maxValue){
44491 this.markInvalid(String.format(this.maxText, this.maxValue));
44498 validate : function()
44500 if(this.disabled || this.allowBlank){
44505 var currency = this.getCurrency();
44507 if(this.validateValue(this.getRawValue()) && currency.length){
44512 this.markInvalid();
44516 getName: function()
44521 beforeBlur : function()
44527 var v = this.parseValue(this.getRawValue());
44534 onBlur : function()
44538 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44539 //this.el.removeClass(this.focusClass);
44542 this.hasFocus = false;
44544 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44548 var v = this.getValue();
44550 if(String(v) !== String(this.startValue)){
44551 this.fireEvent('change', this, v, this.startValue);
44554 this.fireEvent("blur", this);
44557 inputEl : function()
44559 return this.el.select('.roo-money-amount-input', true).first();
44562 currencyEl : function()
44564 return this.el.select('.roo-money-currency-input', true).first();
44567 hiddenEl : function()
44569 return this.el.select('input.hidden-number-input',true).first();
44573 * @class Roo.bootstrap.BezierSignature
44574 * @extends Roo.bootstrap.Component
44575 * Bootstrap BezierSignature class
44576 * This script refer to:
44577 * Title: Signature Pad
44579 * Availability: https://github.com/szimek/signature_pad
44582 * Create a new BezierSignature
44583 * @param {Object} config The config object
44586 Roo.bootstrap.BezierSignature = function(config){
44587 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44593 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44600 mouse_btn_down: true,
44603 * @cfg {int} canvas height
44605 canvas_height: '200px',
44608 * @cfg {float|function} Radius of a single dot.
44613 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44618 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44623 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44628 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44633 * @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.
44635 bg_color: 'rgba(0, 0, 0, 0)',
44638 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44640 dot_color: 'black',
44643 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44645 velocity_filter_weight: 0.7,
44648 * @cfg {function} Callback when stroke begin.
44653 * @cfg {function} Callback when stroke end.
44657 getAutoCreate : function()
44659 var cls = 'roo-signature column';
44662 cls += ' ' + this.cls;
44672 for(var i = 0; i < col_sizes.length; i++) {
44673 if(this[col_sizes[i]]) {
44674 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44684 cls: 'roo-signature-body',
44688 cls: 'roo-signature-body-canvas',
44689 height: this.canvas_height,
44690 width: this.canvas_width
44697 style: 'display: none'
44705 initEvents: function()
44707 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44709 var canvas = this.canvasEl();
44711 // mouse && touch event swapping...
44712 canvas.dom.style.touchAction = 'none';
44713 canvas.dom.style.msTouchAction = 'none';
44715 this.mouse_btn_down = false;
44716 canvas.on('mousedown', this._handleMouseDown, this);
44717 canvas.on('mousemove', this._handleMouseMove, this);
44718 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44720 if (window.PointerEvent) {
44721 canvas.on('pointerdown', this._handleMouseDown, this);
44722 canvas.on('pointermove', this._handleMouseMove, this);
44723 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44726 if ('ontouchstart' in window) {
44727 canvas.on('touchstart', this._handleTouchStart, this);
44728 canvas.on('touchmove', this._handleTouchMove, this);
44729 canvas.on('touchend', this._handleTouchEnd, this);
44732 Roo.EventManager.onWindowResize(this.resize, this, true);
44734 // file input event
44735 this.fileEl().on('change', this.uploadImage, this);
44742 resize: function(){
44744 var canvas = this.canvasEl().dom;
44745 var ctx = this.canvasElCtx();
44746 var img_data = false;
44748 if(canvas.width > 0) {
44749 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44751 // setting canvas width will clean img data
44754 var style = window.getComputedStyle ?
44755 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44757 var padding_left = parseInt(style.paddingLeft) || 0;
44758 var padding_right = parseInt(style.paddingRight) || 0;
44760 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44763 ctx.putImageData(img_data, 0, 0);
44767 _handleMouseDown: function(e)
44769 if (e.browserEvent.which === 1) {
44770 this.mouse_btn_down = true;
44771 this.strokeBegin(e);
44775 _handleMouseMove: function (e)
44777 if (this.mouse_btn_down) {
44778 this.strokeMoveUpdate(e);
44782 _handleMouseUp: function (e)
44784 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44785 this.mouse_btn_down = false;
44790 _handleTouchStart: function (e) {
44792 e.preventDefault();
44793 if (e.browserEvent.targetTouches.length === 1) {
44794 // var touch = e.browserEvent.changedTouches[0];
44795 // this.strokeBegin(touch);
44797 this.strokeBegin(e); // assume e catching the correct xy...
44801 _handleTouchMove: function (e) {
44802 e.preventDefault();
44803 // var touch = event.targetTouches[0];
44804 // _this._strokeMoveUpdate(touch);
44805 this.strokeMoveUpdate(e);
44808 _handleTouchEnd: function (e) {
44809 var wasCanvasTouched = e.target === this.canvasEl().dom;
44810 if (wasCanvasTouched) {
44811 e.preventDefault();
44812 // var touch = event.changedTouches[0];
44813 // _this._strokeEnd(touch);
44818 reset: function () {
44819 this._lastPoints = [];
44820 this._lastVelocity = 0;
44821 this._lastWidth = (this.min_width + this.max_width) / 2;
44822 this.canvasElCtx().fillStyle = this.dot_color;
44825 strokeMoveUpdate: function(e)
44827 this.strokeUpdate(e);
44829 if (this.throttle) {
44830 this.throttleStroke(this.strokeUpdate, this.throttle);
44833 this.strokeUpdate(e);
44837 strokeBegin: function(e)
44839 var newPointGroup = {
44840 color: this.dot_color,
44844 if (typeof this.onBegin === 'function') {
44848 this.curve_data.push(newPointGroup);
44850 this.strokeUpdate(e);
44853 strokeUpdate: function(e)
44855 var rect = this.canvasEl().dom.getBoundingClientRect();
44856 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44857 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44858 var lastPoints = lastPointGroup.points;
44859 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44860 var isLastPointTooClose = lastPoint
44861 ? point.distanceTo(lastPoint) <= this.min_distance
44863 var color = lastPointGroup.color;
44864 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44865 var curve = this.addPoint(point);
44867 this.drawDot({color: color, point: point});
44870 this.drawCurve({color: color, curve: curve});
44880 strokeEnd: function(e)
44882 this.strokeUpdate(e);
44883 if (typeof this.onEnd === 'function') {
44888 addPoint: function (point) {
44889 var _lastPoints = this._lastPoints;
44890 _lastPoints.push(point);
44891 if (_lastPoints.length > 2) {
44892 if (_lastPoints.length === 3) {
44893 _lastPoints.unshift(_lastPoints[0]);
44895 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44896 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44897 _lastPoints.shift();
44903 calculateCurveWidths: function (startPoint, endPoint) {
44904 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44905 (1 - this.velocity_filter_weight) * this._lastVelocity;
44907 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44910 start: this._lastWidth
44913 this._lastVelocity = velocity;
44914 this._lastWidth = newWidth;
44918 drawDot: function (_a) {
44919 var color = _a.color, point = _a.point;
44920 var ctx = this.canvasElCtx();
44921 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44923 this.drawCurveSegment(point.x, point.y, width);
44925 ctx.fillStyle = color;
44929 drawCurve: function (_a) {
44930 var color = _a.color, curve = _a.curve;
44931 var ctx = this.canvasElCtx();
44932 var widthDelta = curve.endWidth - curve.startWidth;
44933 var drawSteps = Math.floor(curve.length()) * 2;
44935 ctx.fillStyle = color;
44936 for (var i = 0; i < drawSteps; i += 1) {
44937 var t = i / drawSteps;
44943 var x = uuu * curve.startPoint.x;
44944 x += 3 * uu * t * curve.control1.x;
44945 x += 3 * u * tt * curve.control2.x;
44946 x += ttt * curve.endPoint.x;
44947 var y = uuu * curve.startPoint.y;
44948 y += 3 * uu * t * curve.control1.y;
44949 y += 3 * u * tt * curve.control2.y;
44950 y += ttt * curve.endPoint.y;
44951 var width = curve.startWidth + ttt * widthDelta;
44952 this.drawCurveSegment(x, y, width);
44958 drawCurveSegment: function (x, y, width) {
44959 var ctx = this.canvasElCtx();
44961 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44962 this.is_empty = false;
44967 var ctx = this.canvasElCtx();
44968 var canvas = this.canvasEl().dom;
44969 ctx.fillStyle = this.bg_color;
44970 ctx.clearRect(0, 0, canvas.width, canvas.height);
44971 ctx.fillRect(0, 0, canvas.width, canvas.height);
44972 this.curve_data = [];
44974 this.is_empty = true;
44979 return this.el.select('input',true).first();
44982 canvasEl: function()
44984 return this.el.select('canvas',true).first();
44987 canvasElCtx: function()
44989 return this.el.select('canvas',true).first().dom.getContext('2d');
44992 getImage: function(type)
44994 if(this.is_empty) {
44999 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45002 drawFromImage: function(img_src)
45004 var img = new Image();
45006 img.onload = function(){
45007 this.canvasElCtx().drawImage(img, 0, 0);
45012 this.is_empty = false;
45015 selectImage: function()
45017 this.fileEl().dom.click();
45020 uploadImage: function(e)
45022 var reader = new FileReader();
45024 reader.onload = function(e){
45025 var img = new Image();
45026 img.onload = function(){
45028 this.canvasElCtx().drawImage(img, 0, 0);
45030 img.src = e.target.result;
45033 reader.readAsDataURL(e.target.files[0]);
45036 // Bezier Point Constructor
45037 Point: (function () {
45038 function Point(x, y, time) {
45041 this.time = time || Date.now();
45043 Point.prototype.distanceTo = function (start) {
45044 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45046 Point.prototype.equals = function (other) {
45047 return this.x === other.x && this.y === other.y && this.time === other.time;
45049 Point.prototype.velocityFrom = function (start) {
45050 return this.time !== start.time
45051 ? this.distanceTo(start) / (this.time - start.time)
45058 // Bezier Constructor
45059 Bezier: (function () {
45060 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45061 this.startPoint = startPoint;
45062 this.control2 = control2;
45063 this.control1 = control1;
45064 this.endPoint = endPoint;
45065 this.startWidth = startWidth;
45066 this.endWidth = endWidth;
45068 Bezier.fromPoints = function (points, widths, scope) {
45069 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45070 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45071 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45073 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45074 var dx1 = s1.x - s2.x;
45075 var dy1 = s1.y - s2.y;
45076 var dx2 = s2.x - s3.x;
45077 var dy2 = s2.y - s3.y;
45078 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45079 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45080 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45081 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45082 var dxm = m1.x - m2.x;
45083 var dym = m1.y - m2.y;
45084 var k = l2 / (l1 + l2);
45085 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45086 var tx = s2.x - cm.x;
45087 var ty = s2.y - cm.y;
45089 c1: new scope.Point(m1.x + tx, m1.y + ty),
45090 c2: new scope.Point(m2.x + tx, m2.y + ty)
45093 Bezier.prototype.length = function () {
45098 for (var i = 0; i <= steps; i += 1) {
45100 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45101 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45103 var xdiff = cx - px;
45104 var ydiff = cy - py;
45105 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45112 Bezier.prototype.point = function (t, start, c1, c2, end) {
45113 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45114 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45115 + (3.0 * c2 * (1.0 - t) * t * t)
45116 + (end * t * t * t);
45121 throttleStroke: function(fn, wait) {
45122 if (wait === void 0) { wait = 250; }
45124 var timeout = null;
45128 var later = function () {
45129 previous = Date.now();
45131 result = fn.apply(storedContext, storedArgs);
45133 storedContext = null;
45137 return function wrapper() {
45139 for (var _i = 0; _i < arguments.length; _i++) {
45140 args[_i] = arguments[_i];
45142 var now = Date.now();
45143 var remaining = wait - (now - previous);
45144 storedContext = this;
45146 if (remaining <= 0 || remaining > wait) {
45148 clearTimeout(timeout);
45152 result = fn.apply(storedContext, storedArgs);
45154 storedContext = null;
45158 else if (!timeout) {
45159 timeout = window.setTimeout(later, remaining);