2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <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>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3046 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3049 * Create a new Input
3050 * @param {Object} config The config object
3053 Roo.bootstrap.Img = function(config){
3054 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3060 * The img click event for the img.
3061 * @param {Roo.EventObject} e
3066 * The when any image loads
3067 * @param {Roo.EventObject} e
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3075 imgResponsive: true,
3084 backgroundContain : false,
3086 getAutoCreate : function()
3088 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089 return this.createSingleImg();
3094 cls: 'roo-image-responsive-group',
3099 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101 if(!_this[size + 'Url']){
3107 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108 html: _this.html || cfg.html,
3109 src: _this[size + 'Url']
3112 img.cls += ' roo-image-responsive-' + size;
3114 var s = ['xs', 'sm', 'md', 'lg'];
3116 s.splice(s.indexOf(size), 1);
3118 Roo.each(s, function(ss){
3119 img.cls += ' hidden-' + ss;
3122 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123 cfg.cls += ' img-' + _this.border;
3127 cfg.alt = _this.alt;
3140 a.target = _this.target;
3144 cfg.cn.push((_this.href) ? a : img);
3151 createSingleImg : function()
3155 cls: (this.imgResponsive) ? 'img-responsive' : '',
3157 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3160 if (this.backgroundContain) {
3161 cfg.cls += ' background-contain';
3164 cfg.html = this.html || cfg.html;
3166 if (this.backgroundContain) {
3167 cfg.style="background-image: url(" + this.src + ')';
3169 cfg.src = this.src || cfg.src;
3172 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173 cfg.cls += ' img-' + this.border;
3190 a.target = this.target;
3195 return (this.href) ? a : cfg;
3198 initEvents: function()
3201 this.el.on('click', this.onClick, this);
3203 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204 this.el.on('load', this.onImageLoad, this);
3206 // not sure if this works.. not tested
3207 this.el.select('img', true).on('load', this.onImageLoad, this);
3212 onClick : function(e)
3214 Roo.log('img onclick');
3215 this.fireEvent('click', this, e);
3217 onImageLoad: function(e)
3219 Roo.log('img load');
3220 this.fireEvent('load', this, e);
3224 * Sets the url of the image - used to update it
3225 * @param {String} url the url of the image
3228 setSrc : function(url)
3232 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233 if (this.backgroundContain) {
3234 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3236 this.el.dom.src = url;
3241 this.el.select('img', true).first().dom.src = url;
3257 * @class Roo.bootstrap.Link
3258 * @extends Roo.bootstrap.Component
3259 * Bootstrap Link Class
3260 * @cfg {String} alt image alternative text
3261 * @cfg {String} href a tag href
3262 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263 * @cfg {String} html the content of the link.
3264 * @cfg {String} anchor name for the anchor link
3265 * @cfg {String} fa - favicon
3267 * @cfg {Boolean} preventDefault (true | false) default false
3271 * Create a new Input
3272 * @param {Object} config The config object
3275 Roo.bootstrap.Link = function(config){
3276 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3282 * The img click event for the img.
3283 * @param {Roo.EventObject} e
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3293 preventDefault: false,
3299 getAutoCreate : function()
3301 var html = this.html || '';
3303 if (this.fa !== false) {
3304 html = '<i class="fa fa-' + this.fa + '"></i>';
3309 // anchor's do not require html/href...
3310 if (this.anchor === false) {
3312 cfg.href = this.href || '#';
3314 cfg.name = this.anchor;
3315 if (this.html !== false || this.fa !== false) {
3318 if (this.href !== false) {
3319 cfg.href = this.href;
3323 if(this.alt !== false){
3328 if(this.target !== false) {
3329 cfg.target = this.target;
3335 initEvents: function() {
3337 if(!this.href || this.preventDefault){
3338 this.el.on('click', this.onClick, this);
3342 onClick : function(e)
3344 if(this.preventDefault){
3347 //Roo.log('img onclick');
3348 this.fireEvent('click', this, e);
3361 * @class Roo.bootstrap.Header
3362 * @extends Roo.bootstrap.Component
3363 * Bootstrap Header class
3364 * @cfg {String} html content of header
3365 * @cfg {Number} level (1|2|3|4|5|6) default 1
3368 * Create a new Header
3369 * @param {Object} config The config object
3373 Roo.bootstrap.Header = function(config){
3374 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3385 getAutoCreate : function(){
3390 tag: 'h' + (1 *this.level),
3391 html: this.html || ''
3403 * Ext JS Library 1.1.1
3404 * Copyright(c) 2006-2007, Ext JS, LLC.
3406 * Originally Released Under LGPL - original licence link has changed is not relivant.
3409 * <script type="text/javascript">
3413 * @class Roo.bootstrap.MenuMgr
3414 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3417 Roo.bootstrap.MenuMgr = function(){
3418 var menus, active, groups = {}, attached = false, lastShow = new Date();
3420 // private - called when first menu is created
3423 active = new Roo.util.MixedCollection();
3424 Roo.get(document).addKeyListener(27, function(){
3425 if(active.length > 0){
3433 if(active && active.length > 0){
3434 var c = active.clone();
3444 if(active.length < 1){
3445 Roo.get(document).un("mouseup", onMouseDown);
3453 var last = active.last();
3454 lastShow = new Date();
3457 Roo.get(document).on("mouseup", onMouseDown);
3462 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463 m.parentMenu.activeChild = m;
3464 }else if(last && last.isVisible()){
3465 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3470 function onBeforeHide(m){
3472 m.activeChild.hide();
3474 if(m.autoHideTimer){
3475 clearTimeout(m.autoHideTimer);
3476 delete m.autoHideTimer;
3481 function onBeforeShow(m){
3482 var pm = m.parentMenu;
3483 if(!pm && !m.allowOtherMenus){
3485 }else if(pm && pm.activeChild && active != m){
3486 pm.activeChild.hide();
3490 // private this should really trigger on mouseup..
3491 function onMouseDown(e){
3492 Roo.log("on Mouse Up");
3494 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495 Roo.log("MenuManager hideAll");
3504 function onBeforeCheck(mi, state){
3506 var g = groups[mi.group];
3507 for(var i = 0, l = g.length; i < l; i++){
3509 g[i].setChecked(false);
3518 * Hides all menus that are currently visible
3520 hideAll : function(){
3525 register : function(menu){
3529 menus[menu.id] = menu;
3530 menu.on("beforehide", onBeforeHide);
3531 menu.on("hide", onHide);
3532 menu.on("beforeshow", onBeforeShow);
3533 menu.on("show", onShow);
3535 if(g && menu.events["checkchange"]){
3539 groups[g].push(menu);
3540 menu.on("checkchange", onCheck);
3545 * Returns a {@link Roo.menu.Menu} object
3546 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547 * be used to generate and return a new Menu instance.
3549 get : function(menu){
3550 if(typeof menu == "string"){ // menu id
3552 }else if(menu.events){ // menu instance
3555 /*else if(typeof menu.length == 'number'){ // array of menu items?
3556 return new Roo.bootstrap.Menu({items:menu});
3557 }else{ // otherwise, must be a config
3558 return new Roo.bootstrap.Menu(menu);
3565 unregister : function(menu){
3566 delete menus[menu.id];
3567 menu.un("beforehide", onBeforeHide);
3568 menu.un("hide", onHide);
3569 menu.un("beforeshow", onBeforeShow);
3570 menu.un("show", onShow);
3572 if(g && menu.events["checkchange"]){
3573 groups[g].remove(menu);
3574 menu.un("checkchange", onCheck);
3579 registerCheckable : function(menuItem){
3580 var g = menuItem.group;
3585 groups[g].push(menuItem);
3586 menuItem.on("beforecheckchange", onBeforeCheck);
3591 unregisterCheckable : function(menuItem){
3592 var g = menuItem.group;
3594 groups[g].remove(menuItem);
3595 menuItem.un("beforecheckchange", onBeforeCheck);
3607 * @class Roo.bootstrap.Menu
3608 * @extends Roo.bootstrap.Component
3609 * Bootstrap Menu class - container for MenuItems
3610 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611 * @cfg {bool} hidden if the menu should be hidden when rendered.
3612 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3613 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3614 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3615 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3619 * @param {Object} config The config object
3623 Roo.bootstrap.Menu = function(config){
3625 if (config.type == 'treeview') {
3626 // normally menu's are drawn attached to the document to handle layering etc..
3627 // however treeview (used by the docs menu is drawn into the parent element)
3628 this.container_method = 'getChildContainer';
3631 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632 if (this.registerMenu && this.type != 'treeview') {
3633 Roo.bootstrap.MenuMgr.register(this);
3640 * Fires before this menu is displayed (return false to block)
3641 * @param {Roo.menu.Menu} this
3646 * Fires before this menu is hidden (return false to block)
3647 * @param {Roo.menu.Menu} this
3652 * Fires after this menu is displayed
3653 * @param {Roo.menu.Menu} this
3658 * Fires after this menu is hidden
3659 * @param {Roo.menu.Menu} this
3664 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665 * @param {Roo.menu.Menu} this
3666 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667 * @param {Roo.EventObject} e
3672 * Fires when the mouse is hovering over this menu
3673 * @param {Roo.menu.Menu} this
3674 * @param {Roo.EventObject} e
3675 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * Fires when the mouse exits this menu
3681 * @param {Roo.menu.Menu} this
3682 * @param {Roo.EventObject} e
3683 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688 * Fires when a menu item contained in this menu is clicked
3689 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690 * @param {Roo.EventObject} e
3694 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3701 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3704 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706 registerMenu : true,
3708 menuItems :false, // stores the menu items..
3718 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720 hideTrigger : false,
3725 getChildContainer : function() {
3729 getAutoCreate : function(){
3731 //if (['right'].indexOf(this.align)!==-1) {
3732 // cfg.cn[1].cls += ' pull-right'
3737 cls : 'dropdown-menu shadow' ,
3738 style : 'z-index:1000'
3742 if (this.type === 'submenu') {
3743 cfg.cls = 'submenu active';
3745 if (this.type === 'treeview') {
3746 cfg.cls = 'treeview-menu';
3751 initEvents : function() {
3753 // Roo.log("ADD event");
3754 // Roo.log(this.triggerEl.dom);
3755 if (this.triggerEl) {
3757 this.triggerEl.on('click', this.onTriggerClick, this);
3759 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761 if (!this.hideTrigger) {
3762 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763 // dropdown toggle on the 'a' in BS4?
3764 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766 this.triggerEl.addClass('dropdown-toggle');
3772 this.el.on('touchstart' , this.onTouch, this);
3774 this.el.on('click' , this.onClick, this);
3776 this.el.on("mouseover", this.onMouseOver, this);
3777 this.el.on("mouseout", this.onMouseOut, this);
3781 findTargetItem : function(e)
3783 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3787 //Roo.log(t); Roo.log(t.id);
3789 //Roo.log(this.menuitems);
3790 return this.menuitems.get(t.id);
3792 //return this.items.get(t.menuItemId);
3798 onTouch : function(e)
3800 Roo.log("menu.onTouch");
3801 //e.stopEvent(); this make the user popdown broken
3805 onClick : function(e)
3807 Roo.log("menu.onClick");
3809 var t = this.findTargetItem(e);
3810 if(!t || t.isContainer){
3815 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3816 if(t == this.activeItem && t.shouldDeactivate(e)){
3817 this.activeItem.deactivate();
3818 delete this.activeItem;
3822 this.setActiveItem(t, true);
3830 Roo.log('pass click event');
3834 this.fireEvent("click", this, t, e);
3838 if(!t.href.length || t.href == '#'){
3839 (function() { _this.hide(); }).defer(100);
3844 onMouseOver : function(e){
3845 var t = this.findTargetItem(e);
3848 // if(t.canActivate && !t.disabled){
3849 // this.setActiveItem(t, true);
3853 this.fireEvent("mouseover", this, e, t);
3855 isVisible : function(){
3856 return !this.hidden;
3858 onMouseOut : function(e){
3859 var t = this.findTargetItem(e);
3862 // if(t == this.activeItem && t.shouldDeactivate(e)){
3863 // this.activeItem.deactivate();
3864 // delete this.activeItem;
3867 this.fireEvent("mouseout", this, e, t);
3872 * Displays this menu relative to another element
3873 * @param {String/HTMLElement/Roo.Element} element The element to align to
3874 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875 * the element (defaults to this.defaultAlign)
3876 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878 show : function(el, pos, parentMenu)
3880 if (false === this.fireEvent("beforeshow", this)) {
3881 Roo.log("show canceled");
3884 this.parentMenu = parentMenu;
3888 this.el.addClass('show'); // show otherwise we do not know how big we are..
3890 var xy = this.el.getAlignToXY(el, pos);
3892 // bl-tl << left align below
3893 // tl-bl << left align
3895 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896 // if it goes to far to the right.. -> align left.
3897 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3900 // was left align - go right?
3901 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3904 // goes down the bottom
3905 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907 var a = this.align.replace('?', '').split('-');
3908 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3912 this.showAt( xy , parentMenu, false);
3915 * Displays this menu at a specific xy position
3916 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919 showAt : function(xy, parentMenu, /* private: */_e){
3920 this.parentMenu = parentMenu;
3925 this.fireEvent("beforeshow", this);
3926 //xy = this.el.adjustForConstraints(xy);
3930 this.hideMenuItems();
3931 this.hidden = false;
3932 if (this.triggerEl) {
3933 this.triggerEl.addClass('open');
3936 this.el.addClass('show');
3940 // reassign x when hitting right
3942 // reassign y when hitting bottom
3944 // but the list may align on trigger left or trigger top... should it be a properity?
3946 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3951 this.fireEvent("show", this);
3957 this.doFocus.defer(50, this);
3961 doFocus : function(){
3963 this.focusEl.focus();
3968 * Hides this menu and optionally all parent menus
3969 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971 hide : function(deep)
3973 if (false === this.fireEvent("beforehide", this)) {
3974 Roo.log("hide canceled");
3977 this.hideMenuItems();
3978 if(this.el && this.isVisible()){
3980 if(this.activeItem){
3981 this.activeItem.deactivate();
3982 this.activeItem = null;
3984 if (this.triggerEl) {
3985 this.triggerEl.removeClass('open');
3988 this.el.removeClass('show');
3990 this.fireEvent("hide", this);
3992 if(deep === true && this.parentMenu){
3993 this.parentMenu.hide(true);
3997 onTriggerClick : function(e)
3999 Roo.log('trigger click');
4001 var target = e.getTarget();
4003 Roo.log(target.nodeName.toLowerCase());
4005 if(target.nodeName.toLowerCase() === 'i'){
4011 onTriggerPress : function(e)
4013 Roo.log('trigger press');
4014 //Roo.log(e.getTarget());
4015 // Roo.log(this.triggerEl.dom);
4017 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018 var pel = Roo.get(e.getTarget());
4019 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020 Roo.log('is treeview or dropdown?');
4024 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4028 if (this.isVisible()) {
4034 this.show(this.triggerEl, this.align, false);
4037 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4044 hideMenuItems : function()
4046 Roo.log("hide Menu Items");
4051 this.el.select('.open',true).each(function(aa) {
4053 aa.removeClass('open');
4057 addxtypeChild : function (tree, cntr) {
4058 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060 this.menuitems.add(comp);
4072 this.getEl().dom.innerHTML = '';
4073 this.menuitems.clear();
4087 * @class Roo.bootstrap.MenuItem
4088 * @extends Roo.bootstrap.Component
4089 * Bootstrap MenuItem class
4090 * @cfg {String} html the menu label
4091 * @cfg {String} href the link
4092 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094 * @cfg {Boolean} active used on sidebars to highlight active itesm
4095 * @cfg {String} fa favicon to show on left of menu item.
4096 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4100 * Create a new MenuItem
4101 * @param {Object} config The config object
4105 Roo.bootstrap.MenuItem = function(config){
4106 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4111 * The raw click event for the entire grid.
4112 * @param {Roo.bootstrap.MenuItem} this
4113 * @param {Roo.EventObject} e
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4123 preventDefault: false,
4124 isContainer : false,
4128 getAutoCreate : function(){
4130 if(this.isContainer){
4133 cls: 'dropdown-menu-item '
4143 cls : 'dropdown-item',
4148 if (this.fa !== false) {
4151 cls : 'fa fa-' + this.fa
4160 cls: 'dropdown-menu-item',
4163 if (this.parent().type == 'treeview') {
4164 cfg.cls = 'treeview-menu';
4167 cfg.cls += ' active';
4172 anc.href = this.href || cfg.cn[0].href ;
4173 ctag.html = this.html || cfg.cn[0].html ;
4177 initEvents: function()
4179 if (this.parent().type == 'treeview') {
4180 this.el.select('a').on('click', this.onClick, this);
4184 this.menu.parentType = this.xtype;
4185 this.menu.triggerEl = this.el;
4186 this.menu = this.addxtype(Roo.apply({}, this.menu));
4190 onClick : function(e)
4192 Roo.log('item on click ');
4194 if(this.preventDefault){
4197 //this.parent().hideMenuItems();
4199 this.fireEvent('click', this, e);
4218 * @class Roo.bootstrap.MenuSeparator
4219 * @extends Roo.bootstrap.Component
4220 * Bootstrap MenuSeparator class
4223 * Create a new MenuItem
4224 * @param {Object} config The config object
4228 Roo.bootstrap.MenuSeparator = function(config){
4229 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4234 getAutoCreate : function(){
4253 * @class Roo.bootstrap.Modal
4254 * @extends Roo.bootstrap.Component
4255 * Bootstrap Modal class
4256 * @cfg {String} title Title of dialog
4257 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4259 * @cfg {Boolean} specificTitle default false
4260 * @cfg {Array} buttons Array of buttons or standard button set..
4261 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262 * @cfg {Boolean} animate default true
4263 * @cfg {Boolean} allow_close default true
4264 * @cfg {Boolean} fitwindow default false
4265 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268 * @cfg {String} size (sm|lg|xl) default empty
4269 * @cfg {Number} max_width set the max width of modal
4270 * @cfg {Boolean} editableTitle can the title be edited
4275 * Create a new Modal Dialog
4276 * @param {Object} config The config object
4279 Roo.bootstrap.Modal = function(config){
4280 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4285 * The raw btnclick event for the button
4286 * @param {Roo.EventObject} e
4291 * Fire when dialog resize
4292 * @param {Roo.bootstrap.Modal} this
4293 * @param {Roo.EventObject} e
4297 * @event titlechanged
4298 * Fire when the editable title has been changed
4299 * @param {Roo.bootstrap.Modal} this
4300 * @param {Roo.EventObject} value
4302 "titlechanged" : true
4305 this.buttons = this.buttons || [];
4308 this.tmpl = Roo.factory(this.tmpl);
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4315 title : 'test dialog',
4325 specificTitle: false,
4327 buttonPosition: 'right',
4349 editableTitle : false,
4351 onRender : function(ct, position)
4353 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4356 var cfg = Roo.apply({}, this.getAutoCreate());
4359 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361 //if (!cfg.name.length) {
4365 cfg.cls += ' ' + this.cls;
4368 cfg.style = this.style;
4370 this.el = Roo.get(document.body).createChild(cfg, position);
4372 //var type = this.el.dom.type;
4375 if(this.tabIndex !== undefined){
4376 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4379 this.dialogEl = this.el.select('.modal-dialog',true).first();
4380 this.bodyEl = this.el.select('.modal-body',true).first();
4381 this.closeEl = this.el.select('.modal-header .close', true).first();
4382 this.headerEl = this.el.select('.modal-header',true).first();
4383 this.titleEl = this.el.select('.modal-title',true).first();
4384 this.footerEl = this.el.select('.modal-footer',true).first();
4386 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388 //this.el.addClass("x-dlg-modal");
4390 if (this.buttons.length) {
4391 Roo.each(this.buttons, function(bb) {
4392 var b = Roo.apply({}, bb);
4393 b.xns = b.xns || Roo.bootstrap;
4394 b.xtype = b.xtype || 'Button';
4395 if (typeof(b.listeners) == 'undefined') {
4396 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4399 var btn = Roo.factory(b);
4401 btn.render(this.getButtonContainer());
4405 // render the children.
4408 if(typeof(this.items) != 'undefined'){
4409 var items = this.items;
4412 for(var i =0;i < items.length;i++) {
4413 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4417 this.items = nitems;
4419 // where are these used - they used to be body/close/footer
4423 //this.el.addClass([this.fieldClass, this.cls]);
4427 getAutoCreate : function()
4429 // we will default to modal-body-overflow - might need to remove or make optional later.
4431 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4432 html : this.html || ''
4437 cls : 'modal-title',
4441 if(this.specificTitle){ // WTF is this?
4446 if (this.allow_close && Roo.bootstrap.version == 3) {
4456 if (this.editableTitle) {
4458 cls: 'form-control roo-editable-title d-none',
4464 if (this.allow_close && Roo.bootstrap.version == 4) {
4474 if(this.size.length){
4475 size = 'modal-' + this.size;
4478 var footer = Roo.bootstrap.version == 3 ?
4480 cls : 'modal-footer',
4484 cls: 'btn-' + this.buttonPosition
4489 { // BS4 uses mr-auto on left buttons....
4490 cls : 'modal-footer'
4501 cls: "modal-dialog " + size,
4504 cls : "modal-content",
4507 cls : 'modal-header',
4522 modal.cls += ' fade';
4528 getChildContainer : function() {
4533 getButtonContainer : function() {
4535 return Roo.bootstrap.version == 4 ?
4536 this.el.select('.modal-footer',true).first()
4537 : this.el.select('.modal-footer div',true).first();
4540 initEvents : function()
4542 if (this.allow_close) {
4543 this.closeEl.on('click', this.hide, this);
4545 Roo.EventManager.onWindowResize(this.resize, this, true);
4546 if (this.editableTitle) {
4547 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4548 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549 this.headerEditEl.on('keyup', function(e) {
4550 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551 this.toggleHeaderInput(false)
4554 this.headerEditEl.on('blur', function(e) {
4555 this.toggleHeaderInput(false)
4564 this.maskEl.setSize(
4565 Roo.lib.Dom.getViewWidth(true),
4566 Roo.lib.Dom.getViewHeight(true)
4569 if (this.fitwindow) {
4571 this.dialogEl.setStyle( { 'max-width' : '100%' });
4573 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4579 if(this.max_width !== 0) {
4581 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4584 this.setSize(w, this.height);
4588 if(this.max_height) {
4589 this.setSize(w,Math.min(
4591 Roo.lib.Dom.getViewportHeight(true) - 60
4597 if(!this.fit_content) {
4598 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4602 this.setSize(w, Math.min(
4604 this.headerEl.getHeight() +
4605 this.footerEl.getHeight() +
4606 this.getChildHeight(this.bodyEl.dom.childNodes),
4607 Roo.lib.Dom.getViewportHeight(true) - 60)
4613 setSize : function(w,h)
4624 if (!this.rendered) {
4627 this.toggleHeaderInput(false);
4628 //this.el.setStyle('display', 'block');
4629 this.el.removeClass('hideing');
4630 this.el.dom.style.display='block';
4632 Roo.get(document.body).addClass('modal-open');
4634 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4637 this.el.addClass('show');
4638 this.el.addClass('in');
4641 this.el.addClass('show');
4642 this.el.addClass('in');
4645 // not sure how we can show data in here..
4647 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4650 Roo.get(document.body).addClass("x-body-masked");
4652 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4653 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654 this.maskEl.dom.style.display = 'block';
4655 this.maskEl.addClass('show');
4660 this.fireEvent('show', this);
4662 // set zindex here - otherwise it appears to be ignored...
4663 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4666 this.items.forEach( function(e) {
4667 e.layout ? e.layout() : false;
4675 if(this.fireEvent("beforehide", this) !== false){
4677 this.maskEl.removeClass('show');
4679 this.maskEl.dom.style.display = '';
4680 Roo.get(document.body).removeClass("x-body-masked");
4681 this.el.removeClass('in');
4682 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684 if(this.animate){ // why
4685 this.el.addClass('hideing');
4686 this.el.removeClass('show');
4688 if (!this.el.hasClass('hideing')) {
4689 return; // it's been shown again...
4692 this.el.dom.style.display='';
4694 Roo.get(document.body).removeClass('modal-open');
4695 this.el.removeClass('hideing');
4699 this.el.removeClass('show');
4700 this.el.dom.style.display='';
4701 Roo.get(document.body).removeClass('modal-open');
4704 this.fireEvent('hide', this);
4707 isVisible : function()
4710 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4714 addButton : function(str, cb)
4718 var b = Roo.apply({}, { html : str } );
4719 b.xns = b.xns || Roo.bootstrap;
4720 b.xtype = b.xtype || 'Button';
4721 if (typeof(b.listeners) == 'undefined') {
4722 b.listeners = { click : cb.createDelegate(this) };
4725 var btn = Roo.factory(b);
4727 btn.render(this.getButtonContainer());
4733 setDefaultButton : function(btn)
4735 //this.el.select('.modal-footer').()
4738 resizeTo: function(w,h)
4740 this.dialogEl.setWidth(w);
4742 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4744 this.bodyEl.setHeight(h - diff);
4746 this.fireEvent('resize', this);
4749 setContentSize : function(w, h)
4753 onButtonClick: function(btn,e)
4756 this.fireEvent('btnclick', btn.name, e);
4759 * Set the title of the Dialog
4760 * @param {String} str new Title
4762 setTitle: function(str) {
4763 this.titleEl.dom.innerHTML = str;
4767 * Set the body of the Dialog
4768 * @param {String} str new Title
4770 setBody: function(str) {
4771 this.bodyEl.dom.innerHTML = str;
4774 * Set the body of the Dialog using the template
4775 * @param {Obj} data - apply this data to the template and replace the body contents.
4777 applyBody: function(obj)
4780 Roo.log("Error - using apply Body without a template");
4783 this.tmpl.overwrite(this.bodyEl, obj);
4786 getChildHeight : function(child_nodes)
4790 child_nodes.length == 0
4795 var child_height = 0;
4797 for(var i = 0; i < child_nodes.length; i++) {
4800 * for modal with tabs...
4801 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803 var layout_childs = child_nodes[i].childNodes;
4805 for(var j = 0; j < layout_childs.length; j++) {
4807 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809 var layout_body_childs = layout_childs[j].childNodes;
4811 for(var k = 0; k < layout_body_childs.length; k++) {
4813 if(layout_body_childs[k].classList.contains('navbar')) {
4814 child_height += layout_body_childs[k].offsetHeight;
4818 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4840 child_height += child_nodes[i].offsetHeight;
4841 // Roo.log(child_nodes[i].offsetHeight);
4844 return child_height;
4846 toggleHeaderInput : function(is_edit)
4848 if (!this.editableTitle) {
4849 return; // not editable.
4851 if (is_edit && this.is_header_editing) {
4852 return; // already editing..
4856 this.headerEditEl.dom.value = this.title;
4857 this.headerEditEl.removeClass('d-none');
4858 this.headerEditEl.dom.focus();
4859 this.titleEl.addClass('d-none');
4861 this.is_header_editing = true;
4864 // flip back to not editing.
4865 this.title = this.headerEditEl.dom.value;
4866 this.headerEditEl.addClass('d-none');
4867 this.titleEl.removeClass('d-none');
4868 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869 this.is_header_editing = false;
4870 this.fireEvent('titlechanged', this, this.title);
4879 Roo.apply(Roo.bootstrap.Modal, {
4881 * Button config that displays a single OK button
4890 * Button config that displays Yes and No buttons
4906 * Button config that displays OK and Cancel buttons
4921 * Button config that displays Yes, No and Cancel buttons
4946 * messagebox - can be used as a replace
4950 * @class Roo.MessageBox
4951 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960 // process text value...
4964 // Show a dialog using config options:
4966 title:'Save Changes?',
4967 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968 buttons: Roo.Msg.YESNOCANCEL,
4975 Roo.bootstrap.MessageBox = function(){
4976 var dlg, opt, mask, waitTimer;
4977 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978 var buttons, activeTextEl, bwidth;
4982 var handleButton = function(button){
4984 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4988 var handleHide = function(){
4990 dlg.el.removeClass(opt.cls);
4993 // Roo.TaskMgr.stop(waitTimer);
4994 // waitTimer = null;
4999 var updateButtons = function(b){
5002 buttons["ok"].hide();
5003 buttons["cancel"].hide();
5004 buttons["yes"].hide();
5005 buttons["no"].hide();
5006 dlg.footerEl.hide();
5010 dlg.footerEl.show();
5011 for(var k in buttons){
5012 if(typeof buttons[k] != "function"){
5015 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016 width += buttons[k].el.getWidth()+15;
5026 var handleEsc = function(d, k, e){
5027 if(opt && opt.closable !== false){
5037 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038 * @return {Roo.BasicDialog} The BasicDialog element
5040 getDialog : function(){
5042 dlg = new Roo.bootstrap.Modal( {
5045 //constraintoviewport:false,
5047 //collapsible : false,
5052 //buttonAlign:"center",
5053 closeClick : function(){
5054 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5057 handleButton("cancel");
5062 dlg.on("hide", handleHide);
5064 //dlg.addKeyListener(27, handleEsc);
5066 this.buttons = buttons;
5067 var bt = this.buttonText;
5068 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073 bodyEl = dlg.bodyEl.createChild({
5075 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076 '<textarea class="roo-mb-textarea"></textarea>' +
5077 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5079 msgEl = bodyEl.dom.firstChild;
5080 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081 textboxEl.enableDisplayMode();
5082 textboxEl.addKeyListener([10,13], function(){
5083 if(dlg.isVisible() && opt && opt.buttons){
5086 }else if(opt.buttons.yes){
5087 handleButton("yes");
5091 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092 textareaEl.enableDisplayMode();
5093 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094 progressEl.enableDisplayMode();
5096 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097 var pf = progressEl.dom.firstChild;
5099 pp = Roo.get(pf.firstChild);
5100 pp.setHeight(pf.offsetHeight);
5108 * Updates the message box body text
5109 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110 * the XHTML-compliant non-breaking space character '&#160;')
5111 * @return {Roo.MessageBox} This message box
5113 updateText : function(text)
5115 if(!dlg.isVisible() && !opt.width){
5116 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119 msgEl.innerHTML = text || ' ';
5121 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124 Math.min(opt.width || cw , this.maxWidth),
5125 Math.max(opt.minWidth || this.minWidth, bwidth)
5128 activeTextEl.setWidth(w);
5130 if(dlg.isVisible()){
5131 dlg.fixedcenter = false;
5133 // to big, make it scroll. = But as usual stupid IE does not support
5136 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140 bodyEl.dom.style.height = '';
5141 bodyEl.dom.style.overflowY = '';
5144 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146 bodyEl.dom.style.overflowX = '';
5149 dlg.setContentSize(w, bodyEl.getHeight());
5150 if(dlg.isVisible()){
5151 dlg.fixedcenter = true;
5157 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5158 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161 * @return {Roo.MessageBox} This message box
5163 updateProgress : function(value, text){
5165 this.updateText(text);
5168 if (pp) { // weird bug on my firefox - for some reason this is not defined
5169 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5176 * Returns true if the message box is currently displayed
5177 * @return {Boolean} True if the message box is visible, else false
5179 isVisible : function(){
5180 return dlg && dlg.isVisible();
5184 * Hides the message box if it is displayed
5187 if(this.isVisible()){
5193 * Displays a new message box, or reinitializes an existing message box, based on the config options
5194 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195 * The following config object properties are supported:
5197 Property Type Description
5198 ---------- --------------- ------------------------------------------------------------------------------------
5199 animEl String/Element An id or Element from which the message box should animate as it opens and
5200 closes (defaults to undefined)
5201 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable Boolean False to hide the top-right close button (defaults to true). Note that
5204 progress and wait dialogs will ignore this property and always hide the
5205 close button as they can only be closed programmatically.
5206 cls String A custom CSS class to apply to the message box element
5207 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5208 displayed (defaults to 75)
5209 fn Function A callback function to execute after closing the dialog. The arguments to the
5210 function will be btn (the name of the button that was clicked, if applicable,
5211 e.g. "ok"), and text (the value of the active text field, if applicable).
5212 Progress and wait dialogs will ignore this option since they do not respond to
5213 user actions and can only be closed programmatically, so any required function
5214 should be called by the same code after it closes the dialog.
5215 icon String A CSS class that provides a background image to be used as an icon for
5216 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5218 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5219 modal Boolean False to allow user interaction with the page while the message box is
5220 displayed (defaults to true)
5221 msg String A string that will replace the existing message box body text (defaults
5222 to the XHTML-compliant non-breaking space character ' ')
5223 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5224 progress Boolean True to display a progress bar (defaults to false)
5225 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5228 title String The title text
5229 value String The string value to set into the active textbox element if displayed
5230 wait Boolean True to display a progress bar (defaults to false)
5231 width Number The width of the dialog in pixels
5238 msg: 'Please enter your address:',
5240 buttons: Roo.MessageBox.OKCANCEL,
5243 animEl: 'addAddressBtn'
5246 * @param {Object} config Configuration options
5247 * @return {Roo.MessageBox} This message box
5249 show : function(options)
5252 // this causes nightmares if you show one dialog after another
5253 // especially on callbacks..
5255 if(this.isVisible()){
5258 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5260 Roo.log("New Dialog Message:" + options.msg )
5261 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5265 var d = this.getDialog();
5267 d.setTitle(opt.title || " ");
5268 d.closeEl.setDisplayed(opt.closable !== false);
5269 activeTextEl = textboxEl;
5270 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5275 textareaEl.setHeight(typeof opt.multiline == "number" ?
5276 opt.multiline : this.defaultTextHeight);
5277 activeTextEl = textareaEl;
5286 progressEl.setDisplayed(opt.progress === true);
5288 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290 this.updateProgress(0);
5291 activeTextEl.dom.value = opt.value || "";
5293 dlg.setDefaultButton(activeTextEl);
5295 var bs = opt.buttons;
5299 }else if(bs && bs.yes){
5300 db = buttons["yes"];
5302 dlg.setDefaultButton(db);
5304 bwidth = updateButtons(opt.buttons);
5305 this.updateText(opt.msg);
5307 d.el.addClass(opt.cls);
5309 d.proxyDrag = opt.proxyDrag === true;
5310 d.modal = opt.modal !== false;
5311 d.mask = opt.modal !== false ? mask : false;
5313 // force it to the end of the z-index stack so it gets a cursor in FF
5314 document.body.appendChild(dlg.el.dom);
5315 d.animateTarget = null;
5316 d.show(options.animEl);
5322 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5323 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324 * and closing the message box when the process is complete.
5325 * @param {String} title The title bar text
5326 * @param {String} msg The message box body text
5327 * @return {Roo.MessageBox} This message box
5329 progress : function(title, msg){
5336 minWidth: this.minProgressWidth,
5343 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344 * If a callback function is passed it will be called after the user clicks the button, and the
5345 * id of the button that was clicked will be passed as the only parameter to the callback
5346 * (could also be the top-right close button).
5347 * @param {String} title The title bar text
5348 * @param {String} msg The message box body text
5349 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350 * @param {Object} scope (optional) The scope of the callback function
5351 * @return {Roo.MessageBox} This message box
5353 alert : function(title, msg, fn, scope)
5368 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5369 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370 * You are responsible for closing the message box when the process is complete.
5371 * @param {String} msg The message box body text
5372 * @param {String} title (optional) The title bar text
5373 * @return {Roo.MessageBox} This message box
5375 wait : function(msg, title){
5386 waitTimer = Roo.TaskMgr.start({
5388 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5396 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399 * @param {String} title The title bar text
5400 * @param {String} msg The message box body text
5401 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402 * @param {Object} scope (optional) The scope of the callback function
5403 * @return {Roo.MessageBox} This message box
5405 confirm : function(title, msg, fn, scope){
5409 buttons: this.YESNO,
5418 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5420 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421 * (could also be the top-right close button) and the text that was entered will be passed as the two
5422 * parameters to the callback.
5423 * @param {String} title The title bar text
5424 * @param {String} msg The message box body text
5425 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426 * @param {Object} scope (optional) The scope of the callback function
5427 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429 * @return {Roo.MessageBox} This message box
5431 prompt : function(title, msg, fn, scope, multiline){
5435 buttons: this.OKCANCEL,
5440 multiline: multiline,
5447 * Button config that displays a single OK button
5452 * Button config that displays Yes and No buttons
5455 YESNO : {yes:true, no:true},
5457 * Button config that displays OK and Cancel buttons
5460 OKCANCEL : {ok:true, cancel:true},
5462 * Button config that displays Yes, No and Cancel buttons
5465 YESNOCANCEL : {yes:true, no:true, cancel:true},
5468 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5471 defaultTextHeight : 75,
5473 * The maximum width in pixels of the message box (defaults to 600)
5478 * The minimum width in pixels of the message box (defaults to 100)
5483 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5484 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5487 minProgressWidth : 250,
5489 * An object containing the default button text strings that can be overriden for localized language support.
5490 * Supported properties are: ok, cancel, yes and no.
5491 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5504 * Shorthand for {@link Roo.MessageBox}
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 * @class Roo.bootstrap.Navbar
5517 * @extends Roo.bootstrap.Component
5518 * Bootstrap Navbar class
5521 * Create a new Navbar
5522 * @param {Object} config The config object
5526 Roo.bootstrap.Navbar = function(config){
5527 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5531 * @event beforetoggle
5532 * Fire before toggle the menu
5533 * @param {Roo.EventObject} e
5535 "beforetoggle" : true
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5548 getAutoCreate : function(){
5551 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5555 initEvents :function ()
5557 //Roo.log(this.el.select('.navbar-toggle',true));
5558 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5565 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567 var size = this.el.getSize();
5568 this.maskEl.setSize(size.width, size.height);
5569 this.maskEl.enableDisplayMode("block");
5578 getChildContainer : function()
5580 if (this.el && this.el.select('.collapse').getCount()) {
5581 return this.el.select('.collapse',true).first();
5596 onToggle : function()
5599 if(this.fireEvent('beforetoggle', this) === false){
5602 var ce = this.el.select('.navbar-collapse',true).first();
5604 if (!ce.hasClass('show')) {
5614 * Expand the navbar pulldown
5616 expand : function ()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5620 if (ce.hasClass('collapsing')) {
5623 ce.dom.style.height = '';
5625 ce.addClass('in'); // old...
5626 ce.removeClass('collapse');
5627 ce.addClass('show');
5628 var h = ce.getHeight();
5630 ce.removeClass('show');
5631 // at this point we should be able to see it..
5632 ce.addClass('collapsing');
5634 ce.setHeight(0); // resize it ...
5635 ce.on('transitionend', function() {
5636 //Roo.log('done transition');
5637 ce.removeClass('collapsing');
5638 ce.addClass('show');
5639 ce.removeClass('collapse');
5641 ce.dom.style.height = '';
5642 }, this, { single: true} );
5644 ce.dom.scrollTop = 0;
5647 * Collapse the navbar pulldown
5649 collapse : function()
5651 var ce = this.el.select('.navbar-collapse',true).first();
5653 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654 // it's collapsed or collapsing..
5657 ce.removeClass('in'); // old...
5658 ce.setHeight(ce.getHeight());
5659 ce.removeClass('show');
5660 ce.addClass('collapsing');
5662 ce.on('transitionend', function() {
5663 ce.dom.style.height = '';
5664 ce.removeClass('collapsing');
5665 ce.addClass('collapse');
5666 }, this, { single: true} );
5686 * @class Roo.bootstrap.NavSimplebar
5687 * @extends Roo.bootstrap.Navbar
5688 * Bootstrap Sidebar class
5690 * @cfg {Boolean} inverse is inverted color
5692 * @cfg {String} type (nav | pills | tabs)
5693 * @cfg {Boolean} arrangement stacked | justified
5694 * @cfg {String} align (left | right) alignment
5696 * @cfg {Boolean} main (true|false) main nav bar? default false
5697 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699 * @cfg {String} tag (header|footer|nav|div) default is nav
5701 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5705 * Create a new Sidebar
5706 * @param {Object} config The config object
5710 Roo.bootstrap.NavSimplebar = function(config){
5711 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5730 getAutoCreate : function(){
5734 tag : this.tag || 'div',
5735 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737 if (['light','white'].indexOf(this.weight) > -1) {
5738 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740 cfg.cls += ' bg-' + this.weight;
5743 cfg.cls += ' navbar-inverse';
5747 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5758 cls: 'nav nav-' + this.xtype,
5764 this.type = this.type || 'nav';
5765 if (['tabs','pills'].indexOf(this.type) != -1) {
5766 cfg.cn[0].cls += ' nav-' + this.type
5770 if (this.type!=='nav') {
5771 Roo.log('nav type must be nav/tabs/pills')
5773 cfg.cn[0].cls += ' navbar-nav'
5779 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780 cfg.cn[0].cls += ' nav-' + this.arrangement;
5784 if (this.align === 'right') {
5785 cfg.cn[0].cls += ' navbar-right';
5810 * navbar-expand-md fixed-top
5814 * @class Roo.bootstrap.NavHeaderbar
5815 * @extends Roo.bootstrap.NavSimplebar
5816 * Bootstrap Sidebar class
5818 * @cfg {String} brand what is brand
5819 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820 * @cfg {String} brand_href href of the brand
5821 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5822 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5827 * Create a new Sidebar
5828 * @param {Object} config The config object
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5844 desktopCenter : false,
5847 getAutoCreate : function(){
5850 tag: this.nav || 'nav',
5851 cls: 'navbar navbar-expand-md',
5857 if (this.desktopCenter) {
5858 cn.push({cls : 'container', cn : []});
5866 cls: 'navbar-toggle navbar-toggler',
5867 'data-toggle': 'collapse',
5872 html: 'Toggle navigation'
5876 cls: 'icon-bar navbar-toggler-icon'
5889 cn.push( Roo.bootstrap.version == 4 ? btn : {
5891 cls: 'navbar-header',
5900 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5904 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906 if (['light','white'].indexOf(this.weight) > -1) {
5907 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909 cfg.cls += ' bg-' + this.weight;
5912 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915 // tag can override this..
5917 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5920 if (this.brand !== '') {
5921 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924 href: this.brand_href ? this.brand_href : '#',
5925 cls: 'navbar-brand',
5933 cfg.cls += ' main-nav';
5941 getHeaderChildContainer : function()
5943 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944 return this.el.select('.navbar-header',true).first();
5947 return this.getChildContainer();
5950 getChildContainer : function()
5953 return this.el.select('.roo-navbar-collapse',true).first();
5958 initEvents : function()
5960 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962 if (this.autohide) {
5967 Roo.get(document).on('scroll',function(e) {
5968 var ns = Roo.get(document).getScroll().top;
5969 var os = prevScroll;
5973 ft.removeClass('slideDown');
5974 ft.addClass('slideUp');
5977 ft.removeClass('slideUp');
5978 ft.addClass('slideDown');
5999 * @class Roo.bootstrap.NavSidebar
6000 * @extends Roo.bootstrap.Navbar
6001 * Bootstrap Sidebar class
6004 * Create a new Sidebar
6005 * @param {Object} config The config object
6009 Roo.bootstrap.NavSidebar = function(config){
6010 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6015 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017 getAutoCreate : function(){
6022 cls: 'sidebar sidebar-nav'
6044 * @class Roo.bootstrap.NavGroup
6045 * @extends Roo.bootstrap.Component
6046 * Bootstrap NavGroup class
6047 * @cfg {String} align (left|right)
6048 * @cfg {Boolean} inverse
6049 * @cfg {String} type (nav|pills|tab) default nav
6050 * @cfg {String} navId - reference Id for navbar.
6051 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6054 * Create a new nav group
6055 * @param {Object} config The config object
6058 Roo.bootstrap.NavGroup = function(config){
6059 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6062 Roo.bootstrap.NavGroup.register(this);
6066 * Fires when the active item changes
6067 * @param {Roo.bootstrap.NavGroup} this
6068 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6088 getAutoCreate : function()
6090 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6096 if (Roo.bootstrap.version == 4) {
6097 if (['tabs','pills'].indexOf(this.type) != -1) {
6098 cfg.cls += ' nav-' + this.type;
6100 // trying to remove so header bar can right align top?
6101 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102 // do not use on header bar...
6103 cfg.cls += ' navbar-nav';
6108 if (['tabs','pills'].indexOf(this.type) != -1) {
6109 cfg.cls += ' nav-' + this.type
6111 if (this.type !== 'nav') {
6112 Roo.log('nav type must be nav/tabs/pills')
6114 cfg.cls += ' navbar-nav'
6118 if (this.parent() && this.parent().sidebar) {
6121 cls: 'dashboard-menu sidebar-menu'
6127 if (this.form === true) {
6130 cls: 'navbar-form form-inline'
6132 //nav navbar-right ml-md-auto
6133 if (this.align === 'right') {
6134 cfg.cls += ' navbar-right ml-md-auto';
6136 cfg.cls += ' navbar-left';
6140 if (this.align === 'right') {
6141 cfg.cls += ' navbar-right ml-md-auto';
6143 cfg.cls += ' mr-auto';
6147 cfg.cls += ' navbar-inverse';
6155 * sets the active Navigation item
6156 * @param {Roo.bootstrap.NavItem} the new current navitem
6158 setActiveItem : function(item)
6161 Roo.each(this.navItems, function(v){
6166 v.setActive(false, true);
6173 item.setActive(true, true);
6174 this.fireEvent('changed', this, item, prev);
6179 * gets the active Navigation item
6180 * @return {Roo.bootstrap.NavItem} the current navitem
6182 getActive : function()
6186 Roo.each(this.navItems, function(v){
6197 indexOfNav : function()
6201 Roo.each(this.navItems, function(v,i){
6212 * adds a Navigation item
6213 * @param {Roo.bootstrap.NavItem} the navitem to add
6215 addItem : function(cfg)
6217 if (this.form && Roo.bootstrap.version == 4) {
6220 var cn = new Roo.bootstrap.NavItem(cfg);
6222 cn.parentId = this.id;
6223 cn.onRender(this.el, null);
6227 * register a Navigation item
6228 * @param {Roo.bootstrap.NavItem} the navitem to add
6230 register : function(item)
6232 this.navItems.push( item);
6233 item.navId = this.navId;
6238 * clear all the Navigation item
6241 clearAll : function()
6244 this.el.dom.innerHTML = '';
6247 getNavItem: function(tabId)
6250 Roo.each(this.navItems, function(e) {
6251 if (e.tabId == tabId) {
6261 setActiveNext : function()
6263 var i = this.indexOfNav(this.getActive());
6264 if (i > this.navItems.length) {
6267 this.setActiveItem(this.navItems[i+1]);
6269 setActivePrev : function()
6271 var i = this.indexOfNav(this.getActive());
6275 this.setActiveItem(this.navItems[i-1]);
6277 clearWasActive : function(except) {
6278 Roo.each(this.navItems, function(e) {
6279 if (e.tabId != except.tabId && e.was_active) {
6280 e.was_active = false;
6287 getWasActive : function ()
6290 Roo.each(this.navItems, function(e) {
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6309 * register a Navigation Group
6310 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312 register : function(navgrp)
6314 this.groups[navgrp.navId] = navgrp;
6318 * fetch a Navigation Group based on the navigation ID
6319 * @param {string} the navgroup to add
6320 * @returns {Roo.bootstrap.NavGroup} the navgroup
6322 get: function(navId) {
6323 if (typeof(this.groups[navId]) == 'undefined') {
6325 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327 return this.groups[navId] ;
6342 * @class Roo.bootstrap.NavItem
6343 * @extends Roo.bootstrap.Component
6344 * Bootstrap Navbar.NavItem class
6345 * @cfg {String} href link to
6346 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347 * @cfg {Boolean} button_outline show and outlined button
6348 * @cfg {String} html content of button
6349 * @cfg {String} badge text inside badge
6350 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351 * @cfg {String} glyphicon DEPRICATED - use fa
6352 * @cfg {String} icon DEPRICATED - use fa
6353 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354 * @cfg {Boolean} active Is item active
6355 * @cfg {Boolean} disabled Is item disabled
6356 * @cfg {String} linkcls Link Class
6357 * @cfg {Boolean} preventDefault (true | false) default false
6358 * @cfg {String} tabId the tab that this item activates.
6359 * @cfg {String} tagtype (a|span) render as a href or span?
6360 * @cfg {Boolean} animateRef (true|false) link to element default false
6363 * Create a new Navbar Item
6364 * @param {Object} config The config object
6366 Roo.bootstrap.NavItem = function(config){
6367 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6372 * The raw click event for the entire grid.
6373 * @param {Roo.EventObject} e
6378 * Fires when the active item active state changes
6379 * @param {Roo.bootstrap.NavItem} this
6380 * @param {boolean} state the new state
6386 * Fires when scroll to element
6387 * @param {Roo.bootstrap.NavItem} this
6388 * @param {Object} options
6389 * @param {Roo.EventObject} e
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6406 preventDefault : false,
6414 button_outline : false,
6418 getAutoCreate : function(){
6425 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6428 cfg.cls += ' active' ;
6430 if (this.disabled) {
6431 cfg.cls += ' disabled';
6435 if (this.button_weight.length) {
6436 cfg.tag = this.href ? 'a' : 'button';
6437 cfg.html = this.html || '';
6438 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440 cfg.href = this.href;
6443 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445 cfg.cls += " nav-html";
6448 // menu .. should add dropdown-menu class - so no need for carat..
6450 if (this.badge !== '') {
6452 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6457 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6461 href : this.href || "#",
6462 html: this.html || '',
6466 if (this.tagtype == 'a') {
6467 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6471 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472 } else if (this.fa) {
6473 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474 } else if(this.glyphicon) {
6475 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6477 cfg.cn[0].cls += " nav-html";
6481 cfg.cn[0].html += " <span class='caret'></span>";
6485 if (this.badge !== '') {
6486 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6494 onRender : function(ct, position)
6496 // Roo.log("Call onRender: " + this.xtype);
6497 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6501 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502 this.navLink = this.el.select('.nav-link',true).first();
6503 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6508 initEvents: function()
6510 if (typeof (this.menu) != 'undefined') {
6511 this.menu.parentType = this.xtype;
6512 this.menu.triggerEl = this.el;
6513 this.menu = this.addxtype(Roo.apply({}, this.menu));
6516 this.el.on('click', this.onClick, this);
6518 //if(this.tagtype == 'span'){
6519 // this.el.select('span',true).on('click', this.onClick, this);
6522 // at this point parent should be available..
6523 this.parent().register(this);
6526 onClick : function(e)
6528 if (e.getTarget('.dropdown-menu-item')) {
6529 // did you click on a menu itemm.... - then don't trigger onclick..
6534 this.preventDefault ||
6537 Roo.log("NavItem - prevent Default?");
6541 if (this.disabled) {
6545 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546 if (tg && tg.transition) {
6547 Roo.log("waiting for the transitionend");
6553 //Roo.log("fire event clicked");
6554 if(this.fireEvent('click', this, e) === false){
6558 if(this.tagtype == 'span'){
6562 //Roo.log(this.href);
6563 var ael = this.el.select('a',true).first();
6566 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569 return; // ignore... - it's a 'hash' to another page.
6571 Roo.log("NavItem - prevent Default?");
6573 this.scrollToElement(e);
6577 var p = this.parent();
6579 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580 if (typeof(p.setActiveItem) !== 'undefined') {
6581 p.setActiveItem(this);
6585 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587 // remove the collapsed menu expand...
6588 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6592 isActive: function () {
6595 setActive : function(state, fire, is_was_active)
6597 if (this.active && !state && this.navId) {
6598 this.was_active = true;
6599 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601 nv.clearWasActive(this);
6605 this.active = state;
6608 this.el.removeClass('active');
6609 this.navLink ? this.navLink.removeClass('active') : false;
6610 } else if (!this.el.hasClass('active')) {
6612 this.el.addClass('active');
6613 if (Roo.bootstrap.version == 4 && this.navLink ) {
6614 this.navLink.addClass('active');
6619 this.fireEvent('changed', this, state);
6622 // show a panel if it's registered and related..
6624 if (!this.navId || !this.tabId || !state || is_was_active) {
6628 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6632 var pan = tg.getPanelByName(this.tabId);
6636 // if we can not flip to new panel - go back to old nav highlight..
6637 if (false == tg.showPanel(pan)) {
6638 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640 var onav = nv.getWasActive();
6642 onav.setActive(true, false, true);
6651 // this should not be here...
6652 setDisabled : function(state)
6654 this.disabled = state;
6656 this.el.removeClass('disabled');
6657 } else if (!this.el.hasClass('disabled')) {
6658 this.el.addClass('disabled');
6664 * Fetch the element to display the tooltip on.
6665 * @return {Roo.Element} defaults to this.el
6667 tooltipEl : function()
6669 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6672 scrollToElement : function(e)
6674 var c = document.body;
6677 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680 c = document.documentElement;
6683 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6689 var o = target.calcOffsetsTo(c);
6696 this.fireEvent('scrollto', this, options, e);
6698 Roo.get(c).scrollTo('top', options.value, true);
6703 * Set the HTML (text content) of the item
6704 * @param {string} html content for the nav item
6706 setHtml : function(html)
6709 this.htmlEl.dom.innerHTML = html;
6721 * <span> icon </span>
6722 * <span> text </span>
6723 * <span>badge </span>
6727 * @class Roo.bootstrap.NavSidebarItem
6728 * @extends Roo.bootstrap.NavItem
6729 * Bootstrap Navbar.NavSidebarItem class
6730 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731 * {Boolean} open is the menu open
6732 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734 * {String} buttonSize (sm|md|lg)the extra classes for the button
6735 * {Boolean} showArrow show arrow next to the text (default true)
6737 * Create a new Navbar Button
6738 * @param {Object} config The config object
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6746 * The raw click event for the entire grid.
6747 * @param {Roo.EventObject} e
6752 * Fires when the active item active state changes
6753 * @param {Roo.bootstrap.NavSidebarItem} this
6754 * @param {boolean} state the new state
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6764 badgeWeight : 'default',
6770 buttonWeight : 'default',
6776 getAutoCreate : function(){
6781 href : this.href || '#',
6787 if(this.buttonView){
6790 href : this.href || '#',
6791 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804 cfg.cls += ' active';
6807 if (this.disabled) {
6808 cfg.cls += ' disabled';
6811 cfg.cls += ' open x-open';
6814 if (this.glyphicon || this.icon) {
6815 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6816 a.cn.push({ tag : 'i', cls : c }) ;
6819 if(!this.buttonView){
6822 html : this.html || ''
6829 if (this.badge !== '') {
6830 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6836 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6839 a.cls += ' dropdown-toggle treeview' ;
6845 initEvents : function()
6847 if (typeof (this.menu) != 'undefined') {
6848 this.menu.parentType = this.xtype;
6849 this.menu.triggerEl = this.el;
6850 this.menu = this.addxtype(Roo.apply({}, this.menu));
6853 this.el.on('click', this.onClick, this);
6855 if(this.badge !== ''){
6856 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6861 onClick : function(e)
6868 if(this.preventDefault){
6872 this.fireEvent('click', this, e);
6875 disable : function()
6877 this.setDisabled(true);
6882 this.setDisabled(false);
6885 setDisabled : function(state)
6887 if(this.disabled == state){
6891 this.disabled = state;
6894 this.el.addClass('disabled');
6898 this.el.removeClass('disabled');
6903 setActive : function(state)
6905 if(this.active == state){
6909 this.active = state;
6912 this.el.addClass('active');
6916 this.el.removeClass('active');
6921 isActive: function ()
6926 setBadge : function(str)
6932 this.badgeEl.dom.innerHTML = str;
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6951 * @class Roo.bootstrap.breadcrumb.Nav
6952 * @extends Roo.bootstrap.Component
6953 * Bootstrap Breadcrumb Nav Class
6955 * @children Roo.bootstrap.breadcrumb.Item
6958 * Create a new breadcrumb.Nav
6959 * @param {Object} config The config object
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6971 getAutoCreate : function()
6988 initEvents: function()
6990 this.olEl = this.el.select('ol',true).first();
6992 getChildContainer : function()
7008 * @class Roo.bootstrap.breadcrumb.Nav
7009 * @extends Roo.bootstrap.Component
7010 * Bootstrap Breadcrumb Nav Class
7012 * @children Roo.bootstrap.breadcrumb.Component
7013 * @cfg {String} html the content of the link.
7014 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015 * @cfg {Boolean} active is it active
7019 * Create a new breadcrumb.Nav
7020 * @param {Object} config The config object
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7029 * The img click event for the img.
7030 * @param {Roo.EventObject} e
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7042 getAutoCreate : function()
7047 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049 if (this.href !== false) {
7056 cfg.html = this.html;
7062 initEvents: function()
7065 this.el.select('a', true).first().on('click',this.onClick, this)
7069 onClick : function(e)
7072 this.fireEvent('click',this, e);
7085 * @class Roo.bootstrap.Row
7086 * @extends Roo.bootstrap.Component
7087 * Bootstrap Row class (contains columns...)
7091 * @param {Object} config The config object
7094 Roo.bootstrap.Row = function(config){
7095 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7100 getAutoCreate : function(){
7119 * @class Roo.bootstrap.Pagination
7120 * @extends Roo.bootstrap.Component
7121 * Bootstrap Pagination class
7122 * @cfg {String} size xs | sm | md | lg
7123 * @cfg {Boolean} inverse false | true
7126 * Create a new Pagination
7127 * @param {Object} config The config object
7130 Roo.bootstrap.Pagination = function(config){
7131 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7140 getAutoCreate : function(){
7146 cfg.cls += ' inverse';
7152 cfg.cls += " " + this.cls;
7170 * @class Roo.bootstrap.PaginationItem
7171 * @extends Roo.bootstrap.Component
7172 * Bootstrap PaginationItem class
7173 * @cfg {String} html text
7174 * @cfg {String} href the link
7175 * @cfg {Boolean} preventDefault (true | false) default true
7176 * @cfg {Boolean} active (true | false) default false
7177 * @cfg {Boolean} disabled default false
7181 * Create a new PaginationItem
7182 * @param {Object} config The config object
7186 Roo.bootstrap.PaginationItem = function(config){
7187 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7192 * The raw click event for the entire grid.
7193 * @param {Roo.EventObject} e
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7203 preventDefault: true,
7208 getAutoCreate : function(){
7214 href : this.href ? this.href : '#',
7215 html : this.html ? this.html : ''
7225 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7229 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7235 initEvents: function() {
7237 this.el.on('click', this.onClick, this);
7240 onClick : function(e)
7242 Roo.log('PaginationItem on click ');
7243 if(this.preventDefault){
7251 this.fireEvent('click', this, e);
7267 * @class Roo.bootstrap.Slider
7268 * @extends Roo.bootstrap.Component
7269 * Bootstrap Slider class
7272 * Create a new Slider
7273 * @param {Object} config The config object
7276 Roo.bootstrap.Slider = function(config){
7277 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7282 getAutoCreate : function(){
7286 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7290 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7302 * Ext JS Library 1.1.1
7303 * Copyright(c) 2006-2007, Ext JS, LLC.
7305 * Originally Released Under LGPL - original licence link has changed is not relivant.
7308 * <script type="text/javascript">
7311 * @extends Roo.dd.DDProxy
7312 * @class Roo.grid.SplitDragZone
7313 * Support for Column Header resizing
7315 * @param {Object} config
7318 // This is a support class used internally by the Grid components
7319 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7321 this.view = grid.getView();
7322 this.proxy = this.view.resizeProxy;
7323 Roo.grid.SplitDragZone.superclass.constructor.call(
7326 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7328 dragElId : Roo.id(this.proxy.dom),
7333 this.setHandleElId(Roo.id(hd));
7334 if (hd2 !== false) {
7335 this.setOuterHandleElId(Roo.id(hd2));
7338 this.scroll = false;
7340 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7341 fly: Roo.Element.fly,
7343 b4StartDrag : function(x, y){
7344 this.view.headersDisabled = true;
7345 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7346 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7348 this.proxy.setHeight(h);
7350 // for old system colWidth really stored the actual width?
7351 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7352 // which in reality did not work.. - it worked only for fixed sizes
7353 // for resizable we need to use actual sizes.
7354 var w = this.cm.getColumnWidth(this.cellIndex);
7355 if (!this.view.mainWrap) {
7357 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7362 // this was w-this.grid.minColumnWidth;
7363 // doesnt really make sense? - w = thie curren width or the rendered one?
7364 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7365 this.resetConstraints();
7366 this.setXConstraint(minw, 1000);
7367 this.setYConstraint(0, 0);
7368 this.minX = x - minw;
7369 this.maxX = x + 1000;
7371 if (!this.view.mainWrap) { // this is Bootstrap code..
7372 this.getDragEl().style.display='block';
7375 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7379 handleMouseDown : function(e){
7380 ev = Roo.EventObject.setEvent(e);
7381 var t = this.fly(ev.getTarget());
7382 if(t.hasClass("x-grid-split")){
7383 this.cellIndex = this.view.getCellIndex(t.dom);
7385 this.cm = this.grid.colModel;
7386 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7387 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7392 endDrag : function(e){
7393 this.view.headersDisabled = false;
7394 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7395 var diff = endX - this.startPos;
7397 var w = this.cm.getColumnWidth(this.cellIndex);
7398 if (!this.view.mainWrap) {
7401 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7404 autoOffset : function(){
7409 * Ext JS Library 1.1.1
7410 * Copyright(c) 2006-2007, Ext JS, LLC.
7412 * Originally Released Under LGPL - original licence link has changed is not relivant.
7415 * <script type="text/javascript">
7419 * @class Roo.grid.AbstractSelectionModel
7420 * @extends Roo.util.Observable
7421 * Abstract base class for grid SelectionModels. It provides the interface that should be
7422 * implemented by descendant classes. This class should not be directly instantiated.
7425 Roo.grid.AbstractSelectionModel = function(){
7426 this.locked = false;
7427 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7430 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7431 /** @ignore Called by the grid automatically. Do not call directly. */
7432 init : function(grid){
7438 * Locks the selections.
7445 * Unlocks the selections.
7447 unlock : function(){
7448 this.locked = false;
7452 * Returns true if the selections are locked.
7455 isLocked : function(){
7460 * Ext JS Library 1.1.1
7461 * Copyright(c) 2006-2007, Ext JS, LLC.
7463 * Originally Released Under LGPL - original licence link has changed is not relivant.
7466 * <script type="text/javascript">
7469 * @extends Roo.grid.AbstractSelectionModel
7470 * @class Roo.grid.RowSelectionModel
7471 * The default SelectionModel used by {@link Roo.grid.Grid}.
7472 * It supports multiple selections and keyboard selection/navigation.
7474 * @param {Object} config
7476 Roo.grid.RowSelectionModel = function(config){
7477 Roo.apply(this, config);
7478 this.selections = new Roo.util.MixedCollection(false, function(o){
7483 this.lastActive = false;
7487 * @event selectionchange
7488 * Fires when the selection changes
7489 * @param {SelectionModel} this
7491 "selectionchange" : true,
7493 * @event afterselectionchange
7494 * Fires after the selection changes (eg. by key press or clicking)
7495 * @param {SelectionModel} this
7497 "afterselectionchange" : true,
7499 * @event beforerowselect
7500 * Fires when a row is selected being selected, return false to cancel.
7501 * @param {SelectionModel} this
7502 * @param {Number} rowIndex The selected index
7503 * @param {Boolean} keepExisting False if other selections will be cleared
7505 "beforerowselect" : true,
7508 * Fires when a row is selected.
7509 * @param {SelectionModel} this
7510 * @param {Number} rowIndex The selected index
7511 * @param {Roo.data.Record} r The record
7515 * @event rowdeselect
7516 * Fires when a row is deselected.
7517 * @param {SelectionModel} this
7518 * @param {Number} rowIndex The selected index
7520 "rowdeselect" : true
7522 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7523 this.locked = false;
7526 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7528 * @cfg {Boolean} singleSelect
7529 * True to allow selection of only one row at a time (defaults to false)
7531 singleSelect : false,
7534 initEvents : function(){
7536 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7537 this.grid.on("mousedown", this.handleMouseDown, this);
7538 }else{ // allow click to work like normal
7539 this.grid.on("rowclick", this.handleDragableRowClick, this);
7541 // bootstrap does not have a view..
7542 var view = this.grid.view ? this.grid.view : this.grid;
7543 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7546 this.selectPrevious(e.shiftKey);
7547 }else if(this.last !== false && this.lastActive !== false){
7548 var last = this.last;
7549 this.selectRange(this.last, this.lastActive-1);
7550 view.focusRow(this.lastActive);
7555 this.selectFirstRow();
7557 this.fireEvent("afterselectionchange", this);
7559 "down" : function(e){
7561 this.selectNext(e.shiftKey);
7562 }else if(this.last !== false && this.lastActive !== false){
7563 var last = this.last;
7564 this.selectRange(this.last, this.lastActive+1);
7565 view.focusRow(this.lastActive);
7570 this.selectFirstRow();
7572 this.fireEvent("afterselectionchange", this);
7578 view.on("refresh", this.onRefresh, this);
7579 view.on("rowupdated", this.onRowUpdated, this);
7580 view.on("rowremoved", this.onRemove, this);
7584 onRefresh : function(){
7585 var ds = this.grid.ds, i, v = this.grid.view;
7586 var s = this.selections;
7588 if((i = ds.indexOfId(r.id)) != -1){
7590 s.add(ds.getAt(i)); // updating the selection relate data
7598 onRemove : function(v, index, r){
7599 this.selections.remove(r);
7603 onRowUpdated : function(v, index, r){
7604 if(this.isSelected(r)){
7605 v.onRowSelect(index);
7611 * @param {Array} records The records to select
7612 * @param {Boolean} keepExisting (optional) True to keep existing selections
7614 selectRecords : function(records, keepExisting){
7616 this.clearSelections();
7618 var ds = this.grid.ds;
7619 for(var i = 0, len = records.length; i < len; i++){
7620 this.selectRow(ds.indexOf(records[i]), true);
7625 * Gets the number of selected rows.
7628 getCount : function(){
7629 return this.selections.length;
7633 * Selects the first row in the grid.
7635 selectFirstRow : function(){
7640 * Select the last row.
7641 * @param {Boolean} keepExisting (optional) True to keep existing selections
7643 selectLastRow : function(keepExisting){
7644 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7648 * Selects the row immediately following the last selected row.
7649 * @param {Boolean} keepExisting (optional) True to keep existing selections
7651 selectNext : function(keepExisting){
7652 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7653 this.selectRow(this.last+1, keepExisting);
7654 var view = this.grid.view ? this.grid.view : this.grid;
7655 view.focusRow(this.last);
7660 * Selects the row that precedes the last selected row.
7661 * @param {Boolean} keepExisting (optional) True to keep existing selections
7663 selectPrevious : function(keepExisting){
7665 this.selectRow(this.last-1, keepExisting);
7666 var view = this.grid.view ? this.grid.view : this.grid;
7667 view.focusRow(this.last);
7672 * Returns the selected records
7673 * @return {Array} Array of selected records
7675 getSelections : function(){
7676 return [].concat(this.selections.items);
7680 * Returns the first selected record.
7683 getSelected : function(){
7684 return this.selections.itemAt(0);
7689 * Clears all selections.
7691 clearSelections : function(fast){
7696 var ds = this.grid.ds;
7697 var s = this.selections;
7699 this.deselectRow(ds.indexOfId(r.id));
7703 this.selections.clear();
7712 selectAll : function(){
7716 this.selections.clear();
7717 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7718 this.selectRow(i, true);
7723 * Returns True if there is a selection.
7726 hasSelection : function(){
7727 return this.selections.length > 0;
7731 * Returns True if the specified row is selected.
7732 * @param {Number/Record} record The record or index of the record to check
7735 isSelected : function(index){
7736 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7737 return (r && this.selections.key(r.id) ? true : false);
7741 * Returns True if the specified record id is selected.
7742 * @param {String} id The id of record to check
7745 isIdSelected : function(id){
7746 return (this.selections.key(id) ? true : false);
7750 handleMouseDown : function(e, t)
7752 var view = this.grid.view ? this.grid.view : this.grid;
7754 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7757 if(e.shiftKey && this.last !== false){
7758 var last = this.last;
7759 this.selectRange(last, rowIndex, e.ctrlKey);
7760 this.last = last; // reset the last
7761 view.focusRow(rowIndex);
7763 var isSelected = this.isSelected(rowIndex);
7764 if(e.button !== 0 && isSelected){
7765 view.focusRow(rowIndex);
7766 }else if(e.ctrlKey && isSelected){
7767 this.deselectRow(rowIndex);
7768 }else if(!isSelected){
7769 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7770 view.focusRow(rowIndex);
7773 this.fireEvent("afterselectionchange", this);
7776 handleDragableRowClick : function(grid, rowIndex, e)
7778 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7779 this.selectRow(rowIndex, false);
7780 var view = this.grid.view ? this.grid.view : this.grid;
7781 view.focusRow(rowIndex);
7782 this.fireEvent("afterselectionchange", this);
7787 * Selects multiple rows.
7788 * @param {Array} rows Array of the indexes of the row to select
7789 * @param {Boolean} keepExisting (optional) True to keep existing selections
7791 selectRows : function(rows, keepExisting){
7793 this.clearSelections();
7795 for(var i = 0, len = rows.length; i < len; i++){
7796 this.selectRow(rows[i], true);
7801 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7802 * @param {Number} startRow The index of the first row in the range
7803 * @param {Number} endRow The index of the last row in the range
7804 * @param {Boolean} keepExisting (optional) True to retain existing selections
7806 selectRange : function(startRow, endRow, keepExisting){
7811 this.clearSelections();
7813 if(startRow <= endRow){
7814 for(var i = startRow; i <= endRow; i++){
7815 this.selectRow(i, true);
7818 for(var i = startRow; i >= endRow; i--){
7819 this.selectRow(i, true);
7825 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7826 * @param {Number} startRow The index of the first row in the range
7827 * @param {Number} endRow The index of the last row in the range
7829 deselectRange : function(startRow, endRow, preventViewNotify){
7833 for(var i = startRow; i <= endRow; i++){
7834 this.deselectRow(i, preventViewNotify);
7840 * @param {Number} row The index of the row to select
7841 * @param {Boolean} keepExisting (optional) True to keep existing selections
7843 selectRow : function(index, keepExisting, preventViewNotify){
7844 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7847 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7848 if(!keepExisting || this.singleSelect){
7849 this.clearSelections();
7851 var r = this.grid.ds.getAt(index);
7852 this.selections.add(r);
7853 this.last = this.lastActive = index;
7854 if(!preventViewNotify){
7855 var view = this.grid.view ? this.grid.view : this.grid;
7856 view.onRowSelect(index);
7858 this.fireEvent("rowselect", this, index, r);
7859 this.fireEvent("selectionchange", this);
7865 * @param {Number} row The index of the row to deselect
7867 deselectRow : function(index, preventViewNotify){
7871 if(this.last == index){
7874 if(this.lastActive == index){
7875 this.lastActive = false;
7877 var r = this.grid.ds.getAt(index);
7878 this.selections.remove(r);
7879 if(!preventViewNotify){
7880 var view = this.grid.view ? this.grid.view : this.grid;
7881 view.onRowDeselect(index);
7883 this.fireEvent("rowdeselect", this, index);
7884 this.fireEvent("selectionchange", this);
7888 restoreLast : function(){
7890 this.last = this._last;
7895 acceptsNav : function(row, col, cm){
7896 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7900 onEditorKey : function(field, e){
7901 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7906 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7908 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7910 }else if(k == e.ENTER && !e.ctrlKey){
7914 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7916 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7918 }else if(k == e.ESC){
7922 g.startEditing(newCell[0], newCell[1]);
7927 * Ext JS Library 1.1.1
7928 * Copyright(c) 2006-2007, Ext JS, LLC.
7930 * Originally Released Under LGPL - original licence link has changed is not relivant.
7933 * <script type="text/javascript">
7938 * @class Roo.grid.ColumnModel
7939 * @extends Roo.util.Observable
7940 * This is the default implementation of a ColumnModel used by the Grid. It defines
7941 * the columns in the grid.
7944 var colModel = new Roo.grid.ColumnModel([
7945 {header: "Ticker", width: 60, sortable: true, locked: true},
7946 {header: "Company Name", width: 150, sortable: true},
7947 {header: "Market Cap.", width: 100, sortable: true},
7948 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7949 {header: "Employees", width: 100, sortable: true, resizable: false}
7954 * The config options listed for this class are options which may appear in each
7955 * individual column definition.
7956 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7958 * @param {Object} config An Array of column config objects. See this class's
7959 * config objects for details.
7961 Roo.grid.ColumnModel = function(config){
7963 * The config passed into the constructor
7965 this.config = []; //config;
7968 // if no id, create one
7969 // if the column does not have a dataIndex mapping,
7970 // map it to the order it is in the config
7971 for(var i = 0, len = config.length; i < len; i++){
7972 this.addColumn(config[i]);
7977 * The width of columns which have no width specified (defaults to 100)
7980 this.defaultWidth = 100;
7983 * Default sortable of columns which have no sortable specified (defaults to false)
7986 this.defaultSortable = false;
7990 * @event widthchange
7991 * Fires when the width of a column changes.
7992 * @param {ColumnModel} this
7993 * @param {Number} columnIndex The column index
7994 * @param {Number} newWidth The new width
7996 "widthchange": true,
7998 * @event headerchange
7999 * Fires when the text of a header changes.
8000 * @param {ColumnModel} this
8001 * @param {Number} columnIndex The column index
8002 * @param {Number} newText The new header text
8004 "headerchange": true,
8006 * @event hiddenchange
8007 * Fires when a column is hidden or "unhidden".
8008 * @param {ColumnModel} this
8009 * @param {Number} columnIndex The column index
8010 * @param {Boolean} hidden true if hidden, false otherwise
8012 "hiddenchange": true,
8014 * @event columnmoved
8015 * Fires when a column is moved.
8016 * @param {ColumnModel} this
8017 * @param {Number} oldIndex
8018 * @param {Number} newIndex
8020 "columnmoved" : true,
8022 * @event columlockchange
8023 * Fires when a column's locked state is changed
8024 * @param {ColumnModel} this
8025 * @param {Number} colIndex
8026 * @param {Boolean} locked true if locked
8028 "columnlockchange" : true
8030 Roo.grid.ColumnModel.superclass.constructor.call(this);
8032 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8034 * @cfg {String} header The header text to display in the Grid view.
8037 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8040 * @cfg {String} smHeader Header at Bootsrap Small width
8043 * @cfg {String} mdHeader Header at Bootsrap Medium width
8046 * @cfg {String} lgHeader Header at Bootsrap Large width
8049 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8052 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8053 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8054 * specified, the column's index is used as an index into the Record's data Array.
8057 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8058 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8061 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8062 * Defaults to the value of the {@link #defaultSortable} property.
8063 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8066 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8069 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8072 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8075 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8078 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8079 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8080 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8081 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8084 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8087 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8090 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8093 * @cfg {String} cursor (Optional)
8096 * @cfg {String} tooltip (Optional)
8099 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8102 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8105 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8108 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8111 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8114 * Returns the id of the column at the specified index.
8115 * @param {Number} index The column index
8116 * @return {String} the id
8118 getColumnId : function(index){
8119 return this.config[index].id;
8123 * Returns the column for a specified id.
8124 * @param {String} id The column id
8125 * @return {Object} the column
8127 getColumnById : function(id){
8128 return this.lookup[id];
8133 * Returns the column Object for a specified dataIndex.
8134 * @param {String} dataIndex The column dataIndex
8135 * @return {Object|Boolean} the column or false if not found
8137 getColumnByDataIndex: function(dataIndex){
8138 var index = this.findColumnIndex(dataIndex);
8139 return index > -1 ? this.config[index] : false;
8143 * Returns the index for a specified column id.
8144 * @param {String} id The column id
8145 * @return {Number} the index, or -1 if not found
8147 getIndexById : function(id){
8148 for(var i = 0, len = this.config.length; i < len; i++){
8149 if(this.config[i].id == id){
8157 * Returns the index for a specified column dataIndex.
8158 * @param {String} dataIndex The column dataIndex
8159 * @return {Number} the index, or -1 if not found
8162 findColumnIndex : function(dataIndex){
8163 for(var i = 0, len = this.config.length; i < len; i++){
8164 if(this.config[i].dataIndex == dataIndex){
8172 moveColumn : function(oldIndex, newIndex){
8173 var c = this.config[oldIndex];
8174 this.config.splice(oldIndex, 1);
8175 this.config.splice(newIndex, 0, c);
8176 this.dataMap = null;
8177 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8180 isLocked : function(colIndex){
8181 return this.config[colIndex].locked === true;
8184 setLocked : function(colIndex, value, suppressEvent){
8185 if(this.isLocked(colIndex) == value){
8188 this.config[colIndex].locked = value;
8190 this.fireEvent("columnlockchange", this, colIndex, value);
8194 getTotalLockedWidth : function(){
8196 for(var i = 0; i < this.config.length; i++){
8197 if(this.isLocked(i) && !this.isHidden(i)){
8198 this.totalWidth += this.getColumnWidth(i);
8204 getLockedCount : function(){
8205 for(var i = 0, len = this.config.length; i < len; i++){
8206 if(!this.isLocked(i)){
8211 return this.config.length;
8215 * Returns the number of columns.
8218 getColumnCount : function(visibleOnly){
8219 if(visibleOnly === true){
8221 for(var i = 0, len = this.config.length; i < len; i++){
8222 if(!this.isHidden(i)){
8228 return this.config.length;
8232 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8233 * @param {Function} fn
8234 * @param {Object} scope (optional)
8235 * @return {Array} result
8237 getColumnsBy : function(fn, scope){
8239 for(var i = 0, len = this.config.length; i < len; i++){
8240 var c = this.config[i];
8241 if(fn.call(scope||this, c, i) === true){
8249 * Returns true if the specified column is sortable.
8250 * @param {Number} col The column index
8253 isSortable : function(col){
8254 if(typeof this.config[col].sortable == "undefined"){
8255 return this.defaultSortable;
8257 return this.config[col].sortable;
8261 * Returns the rendering (formatting) function defined for the column.
8262 * @param {Number} col The column index.
8263 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8265 getRenderer : function(col){
8266 if(!this.config[col].renderer){
8267 return Roo.grid.ColumnModel.defaultRenderer;
8269 return this.config[col].renderer;
8273 * Sets the rendering (formatting) function for a column.
8274 * @param {Number} col The column index
8275 * @param {Function} fn The function to use to process the cell's raw data
8276 * to return HTML markup for the grid view. The render function is called with
8277 * the following parameters:<ul>
8278 * <li>Data value.</li>
8279 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8280 * <li>css A CSS style string to apply to the table cell.</li>
8281 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8282 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8283 * <li>Row index</li>
8284 * <li>Column index</li>
8285 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8287 setRenderer : function(col, fn){
8288 this.config[col].renderer = fn;
8292 * Returns the width for the specified column.
8293 * @param {Number} col The column index
8294 * @param (optional) {String} gridSize bootstrap width size.
8297 getColumnWidth : function(col, gridSize)
8299 var cfg = this.config[col];
8301 if (typeof(gridSize) == 'undefined') {
8302 return cfg.width * 1 || this.defaultWidth;
8304 if (gridSize === false) { // if we set it..
8305 return cfg.width || false;
8307 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8309 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8310 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8313 return cfg[ sizes[i] ];
8320 * Sets the width for a column.
8321 * @param {Number} col The column index
8322 * @param {Number} width The new width
8324 setColumnWidth : function(col, width, suppressEvent){
8325 this.config[col].width = width;
8326 this.totalWidth = null;
8328 this.fireEvent("widthchange", this, col, width);
8333 * Returns the total width of all columns.
8334 * @param {Boolean} includeHidden True to include hidden column widths
8337 getTotalWidth : function(includeHidden){
8338 if(!this.totalWidth){
8339 this.totalWidth = 0;
8340 for(var i = 0, len = this.config.length; i < len; i++){
8341 if(includeHidden || !this.isHidden(i)){
8342 this.totalWidth += this.getColumnWidth(i);
8346 return this.totalWidth;
8350 * Returns the header for the specified column.
8351 * @param {Number} col The column index
8354 getColumnHeader : function(col){
8355 return this.config[col].header;
8359 * Sets the header for a column.
8360 * @param {Number} col The column index
8361 * @param {String} header The new header
8363 setColumnHeader : function(col, header){
8364 this.config[col].header = header;
8365 this.fireEvent("headerchange", this, col, header);
8369 * Returns the tooltip for the specified column.
8370 * @param {Number} col The column index
8373 getColumnTooltip : function(col){
8374 return this.config[col].tooltip;
8377 * Sets the tooltip for a column.
8378 * @param {Number} col The column index
8379 * @param {String} tooltip The new tooltip
8381 setColumnTooltip : function(col, tooltip){
8382 this.config[col].tooltip = tooltip;
8386 * Returns the dataIndex for the specified column.
8387 * @param {Number} col The column index
8390 getDataIndex : function(col){
8391 return this.config[col].dataIndex;
8395 * Sets the dataIndex for a column.
8396 * @param {Number} col The column index
8397 * @param {Number} dataIndex The new dataIndex
8399 setDataIndex : function(col, dataIndex){
8400 this.config[col].dataIndex = dataIndex;
8406 * Returns true if the cell is editable.
8407 * @param {Number} colIndex The column index
8408 * @param {Number} rowIndex The row index - this is nto actually used..?
8411 isCellEditable : function(colIndex, rowIndex){
8412 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8416 * Returns the editor defined for the cell/column.
8417 * return false or null to disable editing.
8418 * @param {Number} colIndex The column index
8419 * @param {Number} rowIndex The row index
8422 getCellEditor : function(colIndex, rowIndex){
8423 return this.config[colIndex].editor;
8427 * Sets if a column is editable.
8428 * @param {Number} col The column index
8429 * @param {Boolean} editable True if the column is editable
8431 setEditable : function(col, editable){
8432 this.config[col].editable = editable;
8437 * Returns true if the column is hidden.
8438 * @param {Number} colIndex The column index
8441 isHidden : function(colIndex){
8442 return this.config[colIndex].hidden;
8447 * Returns true if the column width cannot be changed
8449 isFixed : function(colIndex){
8450 return this.config[colIndex].fixed;
8454 * Returns true if the column can be resized
8457 isResizable : function(colIndex){
8458 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8461 * Sets if a column is hidden.
8462 * @param {Number} colIndex The column index
8463 * @param {Boolean} hidden True if the column is hidden
8465 setHidden : function(colIndex, hidden){
8466 this.config[colIndex].hidden = hidden;
8467 this.totalWidth = null;
8468 this.fireEvent("hiddenchange", this, colIndex, hidden);
8472 * Sets the editor for a column.
8473 * @param {Number} col The column index
8474 * @param {Object} editor The editor object
8476 setEditor : function(col, editor){
8477 this.config[col].editor = editor;
8480 * Add a column (experimental...) - defaults to adding to the end..
8481 * @param {Object} config
8483 addColumn : function(c)
8486 var i = this.config.length;
8489 if(typeof c.dataIndex == "undefined"){
8492 if(typeof c.renderer == "string"){
8493 c.renderer = Roo.util.Format[c.renderer];
8495 if(typeof c.id == "undefined"){
8498 if(c.editor && c.editor.xtype){
8499 c.editor = Roo.factory(c.editor, Roo.grid);
8501 if(c.editor && c.editor.isFormField){
8502 c.editor = new Roo.grid.GridEditor(c.editor);
8504 this.lookup[c.id] = c;
8509 Roo.grid.ColumnModel.defaultRenderer = function(value)
8511 if(typeof value == "object") {
8514 if(typeof value == "string" && value.length < 1){
8518 return String.format("{0}", value);
8521 // Alias for backwards compatibility
8522 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8525 * Ext JS Library 1.1.1
8526 * Copyright(c) 2006-2007, Ext JS, LLC.
8528 * Originally Released Under LGPL - original licence link has changed is not relivant.
8531 * <script type="text/javascript">
8535 * @class Roo.LoadMask
8536 * A simple utility class for generically masking elements while loading data. If the element being masked has
8537 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8538 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8539 * element's UpdateManager load indicator and will be destroyed after the initial load.
8541 * Create a new LoadMask
8542 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8543 * @param {Object} config The config object
8545 Roo.LoadMask = function(el, config){
8546 this.el = Roo.get(el);
8547 Roo.apply(this, config);
8549 this.store.on('beforeload', this.onBeforeLoad, this);
8550 this.store.on('load', this.onLoad, this);
8551 this.store.on('loadexception', this.onLoadException, this);
8552 this.removeMask = false;
8554 var um = this.el.getUpdateManager();
8555 um.showLoadIndicator = false; // disable the default indicator
8556 um.on('beforeupdate', this.onBeforeLoad, this);
8557 um.on('update', this.onLoad, this);
8558 um.on('failure', this.onLoad, this);
8559 this.removeMask = true;
8563 Roo.LoadMask.prototype = {
8565 * @cfg {Boolean} removeMask
8566 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8567 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8571 * The text to display in a centered loading message box (defaults to 'Loading...')
8575 * @cfg {String} msgCls
8576 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8578 msgCls : 'x-mask-loading',
8581 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8587 * Disables the mask to prevent it from being displayed
8589 disable : function(){
8590 this.disabled = true;
8594 * Enables the mask so that it can be displayed
8596 enable : function(){
8597 this.disabled = false;
8600 onLoadException : function()
8604 if (typeof(arguments[3]) != 'undefined') {
8605 Roo.MessageBox.alert("Error loading",arguments[3]);
8609 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8610 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8617 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8622 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8626 onBeforeLoad : function(){
8628 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8633 destroy : function(){
8635 this.store.un('beforeload', this.onBeforeLoad, this);
8636 this.store.un('load', this.onLoad, this);
8637 this.store.un('loadexception', this.onLoadException, this);
8639 var um = this.el.getUpdateManager();
8640 um.un('beforeupdate', this.onBeforeLoad, this);
8641 um.un('update', this.onLoad, this);
8642 um.un('failure', this.onLoad, this);
8646 * @class Roo.bootstrap.Table
8648 * @extends Roo.bootstrap.Component
8649 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8650 * Similar to Roo.grid.Grid
8652 var table = Roo.factory({
8654 xns : Roo.bootstrap,
8655 autoSizeColumns: true,
8662 sortInfo : { direction : 'ASC', field: 'name' },
8664 xtype : 'HttpProxy',
8667 url : 'https://example.com/some.data.url.json'
8670 xtype : 'JsonReader',
8672 fields : [ 'id', 'name', whatever' ],
8679 xtype : 'ColumnModel',
8683 dataIndex : 'is_in_group',
8686 renderer : function(v, x , r) {
8688 return String.format("{0}", v)
8694 xtype : 'RowSelectionModel',
8695 xns : Roo.bootstrap.Table
8696 // you can add listeners to catch selection change here....
8702 grid.render(Roo.get("some-div"));
8705 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8710 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8711 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8712 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8714 * @cfg {String} cls table class
8717 * @cfg {boolean} striped Should the rows be alternative striped
8718 * @cfg {boolean} bordered Add borders to the table
8719 * @cfg {boolean} hover Add hover highlighting
8720 * @cfg {boolean} condensed Format condensed
8721 * @cfg {boolean} responsive Format condensed
8722 * @cfg {Boolean} loadMask (true|false) default false
8723 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8724 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8725 * @cfg {Boolean} rowSelection (true|false) default false
8726 * @cfg {Boolean} cellSelection (true|false) default false
8727 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8728 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8729 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8730 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8731 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8732 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8735 * Create a new Table
8736 * @param {Object} config The config object
8739 Roo.bootstrap.Table = function(config)
8741 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8744 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8745 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8746 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8747 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8749 this.view = this; // compat with grid.
8751 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8753 this.sm.grid = this;
8754 this.selModel = Roo.factory(this.sm, Roo.grid);
8755 this.sm = this.selModel;
8756 this.sm.xmodule = this.xmodule || false;
8759 if (this.cm && typeof(this.cm.config) == 'undefined') {
8760 this.colModel = new Roo.grid.ColumnModel(this.cm);
8761 this.cm = this.colModel;
8762 this.cm.xmodule = this.xmodule || false;
8765 this.store= Roo.factory(this.store, Roo.data);
8766 this.ds = this.store;
8767 this.ds.xmodule = this.xmodule || false;
8770 if (this.footer && this.store) {
8771 this.footer.dataSource = this.ds;
8772 this.footer = Roo.factory(this.footer);
8779 * Fires when a cell is clicked
8780 * @param {Roo.bootstrap.Table} this
8781 * @param {Roo.Element} el
8782 * @param {Number} rowIndex
8783 * @param {Number} columnIndex
8784 * @param {Roo.EventObject} e
8788 * @event celldblclick
8789 * Fires when a cell is double clicked
8790 * @param {Roo.bootstrap.Table} this
8791 * @param {Roo.Element} el
8792 * @param {Number} rowIndex
8793 * @param {Number} columnIndex
8794 * @param {Roo.EventObject} e
8796 "celldblclick" : true,
8799 * Fires when a row is clicked
8800 * @param {Roo.bootstrap.Table} this
8801 * @param {Roo.Element} el
8802 * @param {Number} rowIndex
8803 * @param {Roo.EventObject} e
8807 * @event rowdblclick
8808 * Fires when a row is double clicked
8809 * @param {Roo.bootstrap.Table} this
8810 * @param {Roo.Element} el
8811 * @param {Number} rowIndex
8812 * @param {Roo.EventObject} e
8814 "rowdblclick" : true,
8817 * Fires when a mouseover occur
8818 * @param {Roo.bootstrap.Table} this
8819 * @param {Roo.Element} el
8820 * @param {Number} rowIndex
8821 * @param {Number} columnIndex
8822 * @param {Roo.EventObject} e
8827 * Fires when a mouseout occur
8828 * @param {Roo.bootstrap.Table} this
8829 * @param {Roo.Element} el
8830 * @param {Number} rowIndex
8831 * @param {Number} columnIndex
8832 * @param {Roo.EventObject} e
8837 * Fires when a row is rendered, so you can change add a style to it.
8838 * @param {Roo.bootstrap.Table} this
8839 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8843 * @event rowsrendered
8844 * Fires when all the rows have been rendered
8845 * @param {Roo.bootstrap.Table} this
8847 'rowsrendered' : true,
8849 * @event contextmenu
8850 * The raw contextmenu event for the entire grid.
8851 * @param {Roo.EventObject} e
8853 "contextmenu" : true,
8855 * @event rowcontextmenu
8856 * Fires when a row is right clicked
8857 * @param {Roo.bootstrap.Table} this
8858 * @param {Number} rowIndex
8859 * @param {Roo.EventObject} e
8861 "rowcontextmenu" : true,
8863 * @event cellcontextmenu
8864 * Fires when a cell is right clicked
8865 * @param {Roo.bootstrap.Table} this
8866 * @param {Number} rowIndex
8867 * @param {Number} cellIndex
8868 * @param {Roo.EventObject} e
8870 "cellcontextmenu" : true,
8872 * @event headercontextmenu
8873 * Fires when a header is right clicked
8874 * @param {Roo.bootstrap.Table} this
8875 * @param {Number} columnIndex
8876 * @param {Roo.EventObject} e
8878 "headercontextmenu" : true,
8881 * The raw mousedown event for the entire grid.
8882 * @param {Roo.EventObject} e
8889 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8905 enableColumnResize: true,
8907 rowSelection : false,
8908 cellSelection : false,
8911 minColumnWidth : 50,
8913 // Roo.Element - the tbody
8914 bodyEl: false, // <tbody> Roo.Element - thead element
8915 headEl: false, // <thead> Roo.Element - thead element
8916 resizeProxy : false, // proxy element for dragging?
8920 container: false, // used by gridpanel...
8926 auto_hide_footer : false,
8928 view: false, // actually points to this..
8930 getAutoCreate : function()
8932 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8939 // this get's auto added by panel.Grid
8940 if (this.scrollBody) {
8941 cfg.cls += ' table-body-fixed';
8944 cfg.cls += ' table-striped';
8948 cfg.cls += ' table-hover';
8950 if (this.bordered) {
8951 cfg.cls += ' table-bordered';
8953 if (this.condensed) {
8954 cfg.cls += ' table-condensed';
8957 if (this.responsive) {
8958 cfg.cls += ' table-responsive';
8962 cfg.cls+= ' ' +this.cls;
8968 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8971 if(this.store || this.cm){
8972 if(this.headerShow){
8973 cfg.cn.push(this.renderHeader());
8976 cfg.cn.push(this.renderBody());
8978 if(this.footerShow){
8979 cfg.cn.push(this.renderFooter());
8981 // where does this come from?
8982 //cfg.cls+= ' TableGrid';
8985 return { cn : [ cfg ] };
8988 initEvents : function()
8990 if(!this.store || !this.cm){
8993 if (this.selModel) {
8994 this.selModel.initEvents();
8998 //Roo.log('initEvents with ds!!!!');
9000 this.bodyEl = this.el.select('tbody', true).first();
9001 this.headEl = this.el.select('thead', true).first();
9002 this.mainFoot = this.el.select('tfoot', true).first();
9007 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9008 e.on('click', this.sort, this);
9012 // why is this done????? = it breaks dialogs??
9013 //this.parent().el.setStyle('position', 'relative');
9017 this.footer.parentId = this.id;
9018 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9021 this.el.select('tfoot tr td').first().addClass('hide');
9026 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9029 this.store.on('load', this.onLoad, this);
9030 this.store.on('beforeload', this.onBeforeLoad, this);
9031 this.store.on('update', this.onUpdate, this);
9032 this.store.on('add', this.onAdd, this);
9033 this.store.on("clear", this.clear, this);
9035 this.el.on("contextmenu", this.onContextMenu, this);
9038 this.cm.on("headerchange", this.onHeaderChange, this);
9039 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9041 //?? does bodyEl get replaced on render?
9042 this.bodyEl.on("click", this.onClick, this);
9043 this.bodyEl.on("dblclick", this.onDblClick, this);
9044 this.bodyEl.on('scroll', this.onBodyScroll, this);
9046 // guessing mainbody will work - this relays usually caught by selmodel at present.
9047 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9050 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9053 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9054 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9059 // Compatibility with grid - we implement all the view features at present.
9060 getView : function()
9065 initCSS : function()
9069 var cm = this.cm, styles = [];
9070 this.CSS.removeStyleSheet(this.id + '-cssrules');
9071 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9072 // we can honour xs/sm/md/xl as widths...
9073 // we first have to decide what widht we are currently at...
9074 var sz = Roo.getGridSize();
9078 var cols = []; // visable cols.
9080 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9081 var w = cm.getColumnWidth(i, false);
9083 cols.push( { rel : false, abs : 0 });
9087 cols.push( { rel : false, abs : w });
9089 last = i; // not really..
9092 var w = cm.getColumnWidth(i, sz);
9097 cols.push( { rel : w, abs : false });
9100 var avail = this.bodyEl.dom.clientWidth - total_abs;
9102 var unitWidth = Math.floor(avail / total);
9103 var rem = avail - (unitWidth * total);
9105 var hidden, width, pos = 0 , splithide , left;
9106 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9108 hidden = 'display:none;';
9110 width = 'width:0px;';
9112 if(!cm.isHidden(i)){
9116 // we can honour xs/sm/md/xl ?
9117 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9119 hidden = 'display:none;';
9121 // width should return a small number...
9123 w+=rem; // add the remaining with..
9126 left = "left:" + (pos -4) + "px;";
9127 width = "width:" + w+ "px;";
9130 if (this.responsive) {
9133 hidden = cm.isHidden(i) ? 'display:none' : '';
9134 splithide = 'display: none';
9137 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9140 splithide = 'display:none;';
9143 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9144 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9149 //Roo.log(styles.join(''));
9150 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9156 onContextMenu : function(e, t)
9158 this.processEvent("contextmenu", e);
9161 processEvent : function(name, e)
9163 if (name != 'touchstart' ) {
9164 this.fireEvent(name, e);
9167 var t = e.getTarget();
9169 var cell = Roo.get(t);
9175 if(cell.findParent('tfoot', false, true)){
9179 if(cell.findParent('thead', false, true)){
9181 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9182 cell = Roo.get(t).findParent('th', false, true);
9184 Roo.log("failed to find th in thead?");
9185 Roo.log(e.getTarget());
9190 var cellIndex = cell.dom.cellIndex;
9192 var ename = name == 'touchstart' ? 'click' : name;
9193 this.fireEvent("header" + ename, this, cellIndex, e);
9198 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9199 cell = Roo.get(t).findParent('td', false, true);
9201 Roo.log("failed to find th in tbody?");
9202 Roo.log(e.getTarget());
9207 var row = cell.findParent('tr', false, true);
9208 var cellIndex = cell.dom.cellIndex;
9209 var rowIndex = row.dom.rowIndex - 1;
9213 this.fireEvent("row" + name, this, rowIndex, e);
9217 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9223 onMouseover : function(e, el)
9225 var cell = Roo.get(el);
9231 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9232 cell = cell.findParent('td', false, true);
9235 var row = cell.findParent('tr', false, true);
9236 var cellIndex = cell.dom.cellIndex;
9237 var rowIndex = row.dom.rowIndex - 1; // start from 0
9239 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9243 onMouseout : function(e, el)
9245 var cell = Roo.get(el);
9251 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9252 cell = cell.findParent('td', false, true);
9255 var row = cell.findParent('tr', false, true);
9256 var cellIndex = cell.dom.cellIndex;
9257 var rowIndex = row.dom.rowIndex - 1; // start from 0
9259 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9263 onClick : function(e, el)
9265 var cell = Roo.get(el);
9267 if(!cell || (!this.cellSelection && !this.rowSelection)){
9271 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9272 cell = cell.findParent('td', false, true);
9275 if(!cell || typeof(cell) == 'undefined'){
9279 var row = cell.findParent('tr', false, true);
9281 if(!row || typeof(row) == 'undefined'){
9285 var cellIndex = cell.dom.cellIndex;
9286 var rowIndex = this.getRowIndex(row);
9288 // why??? - should these not be based on SelectionModel?
9289 //if(this.cellSelection){
9290 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9293 //if(this.rowSelection){
9294 this.fireEvent('rowclick', this, row, rowIndex, e);
9299 onDblClick : function(e,el)
9301 var cell = Roo.get(el);
9303 if(!cell || (!this.cellSelection && !this.rowSelection)){
9307 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9308 cell = cell.findParent('td', false, true);
9311 if(!cell || typeof(cell) == 'undefined'){
9315 var row = cell.findParent('tr', false, true);
9317 if(!row || typeof(row) == 'undefined'){
9321 var cellIndex = cell.dom.cellIndex;
9322 var rowIndex = this.getRowIndex(row);
9324 if(this.cellSelection){
9325 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9328 if(this.rowSelection){
9329 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9332 findRowIndex : function(el)
9334 var cell = Roo.get(el);
9338 var row = cell.findParent('tr', false, true);
9340 if(!row || typeof(row) == 'undefined'){
9343 return this.getRowIndex(row);
9345 sort : function(e,el)
9347 var col = Roo.get(el);
9349 if(!col.hasClass('sortable')){
9353 var sort = col.attr('sort');
9356 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9360 this.store.sortInfo = {field : sort, direction : dir};
9363 Roo.log("calling footer first");
9364 this.footer.onClick('first');
9367 this.store.load({ params : { start : 0 } });
9371 renderHeader : function()
9379 this.totalWidth = 0;
9381 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9383 var config = cm.config[i];
9387 cls : 'x-hcol-' + i,
9390 html: cm.getColumnHeader(i)
9393 var tooltip = cm.getColumnTooltip(i);
9395 c.tooltip = tooltip;
9401 if(typeof(config.sortable) != 'undefined' && config.sortable){
9402 c.cls += ' sortable';
9403 c.html = '<i class="fa"></i>' + c.html;
9406 // could use BS4 hidden-..-down
9408 if(typeof(config.lgHeader) != 'undefined'){
9409 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9412 if(typeof(config.mdHeader) != 'undefined'){
9413 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9416 if(typeof(config.smHeader) != 'undefined'){
9417 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9420 if(typeof(config.xsHeader) != 'undefined'){
9421 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9428 if(typeof(config.tooltip) != 'undefined'){
9429 c.tooltip = config.tooltip;
9432 if(typeof(config.colspan) != 'undefined'){
9433 c.colspan = config.colspan;
9436 // hidden is handled by CSS now
9438 if(typeof(config.dataIndex) != 'undefined'){
9439 c.sort = config.dataIndex;
9444 if(typeof(config.align) != 'undefined' && config.align.length){
9445 c.style += ' text-align:' + config.align + ';';
9448 /* width is done in CSS
9449 *if(typeof(config.width) != 'undefined'){
9450 c.style += ' width:' + config.width + 'px;';
9451 this.totalWidth += config.width;
9453 this.totalWidth += 100; // assume minimum of 100 per column?
9457 if(typeof(config.cls) != 'undefined'){
9458 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9460 // this is the bit that doesnt reall work at all...
9462 if (this.responsive) {
9465 ['xs','sm','md','lg'].map(function(size){
9467 if(typeof(config[size]) == 'undefined'){
9471 if (!config[size]) { // 0 = hidden
9472 // BS 4 '0' is treated as hide that column and below.
9473 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9477 c.cls += ' col-' + size + '-' + config[size] + (
9478 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9486 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9497 renderBody : function()
9507 colspan : this.cm.getColumnCount()
9517 renderFooter : function()
9527 colspan : this.cm.getColumnCount()
9541 // Roo.log('ds onload');
9546 var ds = this.store;
9548 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9549 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9550 if (_this.store.sortInfo) {
9552 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9553 e.select('i', true).addClass(['fa-arrow-up']);
9556 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9557 e.select('i', true).addClass(['fa-arrow-down']);
9562 var tbody = this.bodyEl;
9564 if(ds.getCount() > 0){
9565 ds.data.each(function(d,rowIndex){
9566 var row = this.renderRow(cm, ds, rowIndex);
9568 tbody.createChild(row);
9572 if(row.cellObjects.length){
9573 Roo.each(row.cellObjects, function(r){
9574 _this.renderCellObject(r);
9581 var tfoot = this.el.select('tfoot', true).first();
9583 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9585 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9587 var total = this.ds.getTotalCount();
9589 if(this.footer.pageSize < total){
9590 this.mainFoot.show();
9594 Roo.each(this.el.select('tbody td', true).elements, function(e){
9595 e.on('mouseover', _this.onMouseover, _this);
9598 Roo.each(this.el.select('tbody td', true).elements, function(e){
9599 e.on('mouseout', _this.onMouseout, _this);
9601 this.fireEvent('rowsrendered', this);
9605 this.initCSS(); /// resize cols
9611 onUpdate : function(ds,record)
9613 this.refreshRow(record);
9617 onRemove : function(ds, record, index, isUpdate){
9618 if(isUpdate !== true){
9619 this.fireEvent("beforerowremoved", this, index, record);
9621 var bt = this.bodyEl.dom;
9623 var rows = this.el.select('tbody > tr', true).elements;
9625 if(typeof(rows[index]) != 'undefined'){
9626 bt.removeChild(rows[index].dom);
9629 // if(bt.rows[index]){
9630 // bt.removeChild(bt.rows[index]);
9633 if(isUpdate !== true){
9634 //this.stripeRows(index);
9635 //this.syncRowHeights(index, index);
9637 this.fireEvent("rowremoved", this, index, record);
9641 onAdd : function(ds, records, rowIndex)
9643 //Roo.log('on Add called');
9644 // - note this does not handle multiple adding very well..
9645 var bt = this.bodyEl.dom;
9646 for (var i =0 ; i < records.length;i++) {
9647 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9648 //Roo.log(records[i]);
9649 //Roo.log(this.store.getAt(rowIndex+i));
9650 this.insertRow(this.store, rowIndex + i, false);
9657 refreshRow : function(record){
9658 var ds = this.store, index;
9659 if(typeof record == 'number'){
9661 record = ds.getAt(index);
9663 index = ds.indexOf(record);
9665 return; // should not happen - but seems to
9668 this.insertRow(ds, index, true);
9670 this.onRemove(ds, record, index+1, true);
9672 //this.syncRowHeights(index, index);
9674 this.fireEvent("rowupdated", this, index, record);
9676 // private - called by RowSelection
9677 onRowSelect : function(rowIndex){
9678 var row = this.getRowDom(rowIndex);
9679 row.addClass(['bg-info','info']);
9681 // private - called by RowSelection
9682 onRowDeselect : function(rowIndex)
9687 var row = this.getRowDom(rowIndex);
9688 row.removeClass(['bg-info','info']);
9691 * Focuses the specified row.
9692 * @param {Number} row The row index
9694 focusRow : function(row)
9696 //Roo.log('GridView.focusRow');
9697 var x = this.bodyEl.dom.scrollLeft;
9698 this.focusCell(row, 0, false);
9699 this.bodyEl.dom.scrollLeft = x;
9703 * Focuses the specified cell.
9704 * @param {Number} row The row index
9705 * @param {Number} col The column index
9706 * @param {Boolean} hscroll false to disable horizontal scrolling
9708 focusCell : function(row, col, hscroll)
9710 //Roo.log('GridView.focusCell');
9711 var el = this.ensureVisible(row, col, hscroll);
9712 // not sure what focusEL achives = it's a <a> pos relative
9713 //this.focusEl.alignTo(el, "tl-tl");
9715 // this.focusEl.focus();
9717 // this.focusEl.focus.defer(1, this.focusEl);
9722 * Scrolls the specified cell into view
9723 * @param {Number} row The row index
9724 * @param {Number} col The column index
9725 * @param {Boolean} hscroll false to disable horizontal scrolling
9727 ensureVisible : function(row, col, hscroll)
9729 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9730 //return null; //disable for testing.
9731 if(typeof row != "number"){
9734 if(row < 0 && row >= this.ds.getCount()){
9737 col = (col !== undefined ? col : 0);
9739 while(cm.isHidden(col)){
9743 var el = this.getCellDom(row, col);
9747 var c = this.bodyEl.dom;
9749 var ctop = parseInt(el.offsetTop, 10);
9750 var cleft = parseInt(el.offsetLeft, 10);
9751 var cbot = ctop + el.offsetHeight;
9752 var cright = cleft + el.offsetWidth;
9754 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9755 var ch = 0; //?? header is not withing the area?
9756 var stop = parseInt(c.scrollTop, 10);
9757 var sleft = parseInt(c.scrollLeft, 10);
9758 var sbot = stop + ch;
9759 var sright = sleft + c.clientWidth;
9761 Roo.log('GridView.ensureVisible:' +
9763 ' c.clientHeight:' + c.clientHeight +
9764 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9773 //Roo.log("set scrolltop to ctop DISABLE?");
9774 }else if(cbot > sbot){
9775 //Roo.log("set scrolltop to cbot-ch");
9776 c.scrollTop = cbot-ch;
9779 if(hscroll !== false){
9781 c.scrollLeft = cleft;
9782 }else if(cright > sright){
9783 c.scrollLeft = cright-c.clientWidth;
9791 insertRow : function(dm, rowIndex, isUpdate){
9794 this.fireEvent("beforerowsinserted", this, rowIndex);
9796 //var s = this.getScrollState();
9797 var row = this.renderRow(this.cm, this.store, rowIndex);
9798 // insert before rowIndex..
9799 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9803 if(row.cellObjects.length){
9804 Roo.each(row.cellObjects, function(r){
9805 _this.renderCellObject(r);
9810 this.fireEvent("rowsinserted", this, rowIndex);
9811 //this.syncRowHeights(firstRow, lastRow);
9812 //this.stripeRows(firstRow);
9819 getRowDom : function(rowIndex)
9821 var rows = this.el.select('tbody > tr', true).elements;
9823 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9826 getCellDom : function(rowIndex, colIndex)
9828 var row = this.getRowDom(rowIndex);
9829 if (row === false) {
9832 var cols = row.select('td', true).elements;
9833 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9837 // returns the object tree for a tr..
9840 renderRow : function(cm, ds, rowIndex)
9842 var d = ds.getAt(rowIndex);
9846 cls : 'x-row-' + rowIndex,
9850 var cellObjects = [];
9852 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9853 var config = cm.config[i];
9855 var renderer = cm.getRenderer(i);
9859 if(typeof(renderer) !== 'undefined'){
9860 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9862 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9863 // and are rendered into the cells after the row is rendered - using the id for the element.
9865 if(typeof(value) === 'object'){
9875 rowIndex : rowIndex,
9880 this.fireEvent('rowclass', this, rowcfg);
9884 // this might end up displaying HTML?
9885 // this is too messy... - better to only do it on columsn you know are going to be too long
9886 //tooltip : (typeof(value) === 'object') ? '' : value,
9887 cls : rowcfg.rowClass + ' x-col-' + i,
9889 html: (typeof(value) === 'object') ? '' : value
9896 if(typeof(config.colspan) != 'undefined'){
9897 td.colspan = config.colspan;
9902 if(typeof(config.align) != 'undefined' && config.align.length){
9903 td.style += ' text-align:' + config.align + ';';
9905 if(typeof(config.valign) != 'undefined' && config.valign.length){
9906 td.style += ' vertical-align:' + config.valign + ';';
9909 if(typeof(config.width) != 'undefined'){
9910 td.style += ' width:' + config.width + 'px;';
9914 if(typeof(config.cursor) != 'undefined'){
9915 td.style += ' cursor:' + config.cursor + ';';
9918 if(typeof(config.cls) != 'undefined'){
9919 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9921 if (this.responsive) {
9922 ['xs','sm','md','lg'].map(function(size){
9924 if(typeof(config[size]) == 'undefined'){
9930 if (!config[size]) { // 0 = hidden
9931 // BS 4 '0' is treated as hide that column and below.
9932 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9936 td.cls += ' col-' + size + '-' + config[size] + (
9937 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9947 row.cellObjects = cellObjects;
9955 onBeforeLoad : function()
9964 this.el.select('tbody', true).first().dom.innerHTML = '';
9967 * Show or hide a row.
9968 * @param {Number} rowIndex to show or hide
9969 * @param {Boolean} state hide
9971 setRowVisibility : function(rowIndex, state)
9973 var bt = this.bodyEl.dom;
9975 var rows = this.el.select('tbody > tr', true).elements;
9977 if(typeof(rows[rowIndex]) == 'undefined'){
9980 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9985 getSelectionModel : function(){
9987 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9989 return this.selModel;
9992 * Render the Roo.bootstrap object from renderder
9994 renderCellObject : function(r)
9998 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10000 var t = r.cfg.render(r.container);
10003 Roo.each(r.cfg.cn, function(c){
10005 container: t.getChildContainer(),
10008 _this.renderCellObject(child);
10013 * get the Row Index from a dom element.
10014 * @param {Roo.Element} row The row to look for
10015 * @returns {Number} the row
10017 getRowIndex : function(row)
10021 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10032 * get the header TH element for columnIndex
10033 * @param {Number} columnIndex
10034 * @returns {Roo.Element}
10036 getHeaderIndex: function(colIndex)
10038 var cols = this.headEl.select('th', true).elements;
10039 return cols[colIndex];
10042 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10043 * @param {domElement} cell to look for
10044 * @returns {Number} the column
10046 getCellIndex : function(cell)
10048 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10050 return parseInt(id[1], 10);
10055 * Returns the grid's underlying element = used by panel.Grid
10056 * @return {Element} The element
10058 getGridEl : function(){
10062 * Forces a resize - used by panel.Grid
10063 * @return {Element} The element
10065 autoSize : function()
10067 //var ctr = Roo.get(this.container.dom.parentElement);
10068 var ctr = Roo.get(this.el.dom);
10070 var thd = this.getGridEl().select('thead',true).first();
10071 var tbd = this.getGridEl().select('tbody', true).first();
10072 var tfd = this.getGridEl().select('tfoot', true).first();
10074 var cw = ctr.getWidth();
10075 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10079 tbd.setWidth(ctr.getWidth());
10080 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10081 // this needs fixing for various usage - currently only hydra job advers I think..
10083 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10085 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10088 cw = Math.max(cw, this.totalWidth);
10089 this.getGridEl().select('tbody tr',true).setWidth(cw);
10092 // resize 'expandable coloumn?
10094 return; // we doe not have a view in this design..
10097 onBodyScroll: function()
10099 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10101 this.headEl.setStyle({
10102 'position' : 'relative',
10103 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10109 var scrollHeight = this.bodyEl.dom.scrollHeight;
10111 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10113 var height = this.bodyEl.getHeight();
10115 if(scrollHeight - height == scrollTop) {
10117 var total = this.ds.getTotalCount();
10119 if(this.footer.cursor + this.footer.pageSize < total){
10121 this.footer.ds.load({
10123 start : this.footer.cursor + this.footer.pageSize,
10124 limit : this.footer.pageSize
10133 onColumnSplitterMoved : function(i, diff)
10135 this.userResized = true;
10137 var cm = this.colModel;
10139 var w = this.getHeaderIndex(i).getWidth() + diff;
10142 cm.setColumnWidth(i, w, true);
10144 //var cid = cm.getColumnId(i); << not used in this version?
10145 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10147 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10148 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10149 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10151 //this.updateSplitters();
10152 //this.layout(); << ??
10153 this.fireEvent("columnresize", i, w);
10155 onHeaderChange : function()
10157 var header = this.renderHeader();
10158 var table = this.el.select('table', true).first();
10160 this.headEl.remove();
10161 this.headEl = table.createChild(header, this.bodyEl, false);
10163 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10164 e.on('click', this.sort, this);
10167 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10168 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10173 onHiddenChange : function(colModel, colIndex, hidden)
10176 this.cm.setHidden()
10177 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10178 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10180 this.CSS.updateRule(thSelector, "display", "");
10181 this.CSS.updateRule(tdSelector, "display", "");
10184 this.CSS.updateRule(thSelector, "display", "none");
10185 this.CSS.updateRule(tdSelector, "display", "none");
10188 // onload calls initCSS()
10189 this.onHeaderChange();
10193 setColumnWidth: function(col_index, width)
10195 // width = "md-2 xs-2..."
10196 if(!this.colModel.config[col_index]) {
10200 var w = width.split(" ");
10202 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10204 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10207 for(var j = 0; j < w.length; j++) {
10213 var size_cls = w[j].split("-");
10215 if(!Number.isInteger(size_cls[1] * 1)) {
10219 if(!this.colModel.config[col_index][size_cls[0]]) {
10223 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10227 h_row[0].classList.replace(
10228 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10229 "col-"+size_cls[0]+"-"+size_cls[1]
10232 for(var i = 0; i < rows.length; i++) {
10234 var size_cls = w[j].split("-");
10236 if(!Number.isInteger(size_cls[1] * 1)) {
10240 if(!this.colModel.config[col_index][size_cls[0]]) {
10244 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10248 rows[i].classList.replace(
10249 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10250 "col-"+size_cls[0]+"-"+size_cls[1]
10254 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10259 // currently only used to find the split on drag..
10260 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10265 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10266 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10275 * @class Roo.bootstrap.TableCell
10276 * @extends Roo.bootstrap.Component
10277 * Bootstrap TableCell class
10278 * @cfg {String} html cell contain text
10279 * @cfg {String} cls cell class
10280 * @cfg {String} tag cell tag (td|th) default td
10281 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10282 * @cfg {String} align Aligns the content in a cell
10283 * @cfg {String} axis Categorizes cells
10284 * @cfg {String} bgcolor Specifies the background color of a cell
10285 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10286 * @cfg {Number} colspan Specifies the number of columns a cell should span
10287 * @cfg {String} headers Specifies one or more header cells a cell is related to
10288 * @cfg {Number} height Sets the height of a cell
10289 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10290 * @cfg {Number} rowspan Sets the number of rows a cell should span
10291 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10292 * @cfg {String} valign Vertical aligns the content in a cell
10293 * @cfg {Number} width Specifies the width of a cell
10296 * Create a new TableCell
10297 * @param {Object} config The config object
10300 Roo.bootstrap.TableCell = function(config){
10301 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10304 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10324 getAutoCreate : function(){
10325 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10332 cfg.tag = this.tag;
10345 cfg.align=this.align
10350 if (this.bgcolor) {
10351 cfg.bgcolor=this.bgcolor
10353 if (this.charoff) {
10354 cfg.charoff=this.charoff
10356 if (this.colspan) {
10357 cfg.colspan=this.colspan
10359 if (this.headers) {
10360 cfg.headers=this.headers
10363 cfg.height=this.height
10366 cfg.nowrap=this.nowrap
10368 if (this.rowspan) {
10369 cfg.rowspan=this.rowspan
10372 cfg.scope=this.scope
10375 cfg.valign=this.valign
10378 cfg.width=this.width
10397 * @class Roo.bootstrap.TableRow
10398 * @extends Roo.bootstrap.Component
10399 * Bootstrap TableRow class
10400 * @cfg {String} cls row class
10401 * @cfg {String} align Aligns the content in a table row
10402 * @cfg {String} bgcolor Specifies a background color for a table row
10403 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10404 * @cfg {String} valign Vertical aligns the content in a table row
10407 * Create a new TableRow
10408 * @param {Object} config The config object
10411 Roo.bootstrap.TableRow = function(config){
10412 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10415 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10423 getAutoCreate : function(){
10424 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10431 cfg.cls = this.cls;
10434 cfg.align = this.align;
10437 cfg.bgcolor = this.bgcolor;
10440 cfg.charoff = this.charoff;
10443 cfg.valign = this.valign;
10461 * @class Roo.bootstrap.TableBody
10462 * @extends Roo.bootstrap.Component
10463 * Bootstrap TableBody class
10464 * @cfg {String} cls element class
10465 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10466 * @cfg {String} align Aligns the content inside the element
10467 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10468 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10471 * Create a new TableBody
10472 * @param {Object} config The config object
10475 Roo.bootstrap.TableBody = function(config){
10476 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10479 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10487 getAutoCreate : function(){
10488 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10498 cfg.tag = this.tag;
10502 cfg.align = this.align;
10505 cfg.charoff = this.charoff;
10508 cfg.valign = this.valign;
10515 // initEvents : function()
10518 // if(!this.store){
10522 // this.store = Roo.factory(this.store, Roo.data);
10523 // this.store.on('load', this.onLoad, this);
10525 // this.store.load();
10529 // onLoad: function ()
10531 // this.fireEvent('load', this);
10541 * Ext JS Library 1.1.1
10542 * Copyright(c) 2006-2007, Ext JS, LLC.
10544 * Originally Released Under LGPL - original licence link has changed is not relivant.
10547 * <script type="text/javascript">
10550 // as we use this in bootstrap.
10551 Roo.namespace('Roo.form');
10553 * @class Roo.form.Action
10554 * Internal Class used to handle form actions
10556 * @param {Roo.form.BasicForm} el The form element or its id
10557 * @param {Object} config Configuration options
10562 // define the action interface
10563 Roo.form.Action = function(form, options){
10565 this.options = options || {};
10568 * Client Validation Failed
10571 Roo.form.Action.CLIENT_INVALID = 'client';
10573 * Server Validation Failed
10576 Roo.form.Action.SERVER_INVALID = 'server';
10578 * Connect to Server Failed
10581 Roo.form.Action.CONNECT_FAILURE = 'connect';
10583 * Reading Data from Server Failed
10586 Roo.form.Action.LOAD_FAILURE = 'load';
10588 Roo.form.Action.prototype = {
10590 failureType : undefined,
10591 response : undefined,
10592 result : undefined,
10594 // interface method
10595 run : function(options){
10599 // interface method
10600 success : function(response){
10604 // interface method
10605 handleResponse : function(response){
10609 // default connection failure
10610 failure : function(response){
10612 this.response = response;
10613 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10614 this.form.afterAction(this, false);
10617 processResponse : function(response){
10618 this.response = response;
10619 if(!response.responseText){
10622 this.result = this.handleResponse(response);
10623 return this.result;
10626 // utility functions used internally
10627 getUrl : function(appendParams){
10628 var url = this.options.url || this.form.url || this.form.el.dom.action;
10630 var p = this.getParams();
10632 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10638 getMethod : function(){
10639 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10642 getParams : function(){
10643 var bp = this.form.baseParams;
10644 var p = this.options.params;
10646 if(typeof p == "object"){
10647 p = Roo.urlEncode(Roo.applyIf(p, bp));
10648 }else if(typeof p == 'string' && bp){
10649 p += '&' + Roo.urlEncode(bp);
10652 p = Roo.urlEncode(bp);
10657 createCallback : function(){
10659 success: this.success,
10660 failure: this.failure,
10662 timeout: (this.form.timeout*1000),
10663 upload: this.form.fileUpload ? this.success : undefined
10668 Roo.form.Action.Submit = function(form, options){
10669 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10672 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10675 haveProgress : false,
10676 uploadComplete : false,
10678 // uploadProgress indicator.
10679 uploadProgress : function()
10681 if (!this.form.progressUrl) {
10685 if (!this.haveProgress) {
10686 Roo.MessageBox.progress("Uploading", "Uploading");
10688 if (this.uploadComplete) {
10689 Roo.MessageBox.hide();
10693 this.haveProgress = true;
10695 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10697 var c = new Roo.data.Connection();
10699 url : this.form.progressUrl,
10704 success : function(req){
10705 //console.log(data);
10709 rdata = Roo.decode(req.responseText)
10711 Roo.log("Invalid data from server..");
10715 if (!rdata || !rdata.success) {
10717 Roo.MessageBox.alert(Roo.encode(rdata));
10720 var data = rdata.data;
10722 if (this.uploadComplete) {
10723 Roo.MessageBox.hide();
10728 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10729 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10732 this.uploadProgress.defer(2000,this);
10735 failure: function(data) {
10736 Roo.log('progress url failed ');
10747 // run get Values on the form, so it syncs any secondary forms.
10748 this.form.getValues();
10750 var o = this.options;
10751 var method = this.getMethod();
10752 var isPost = method == 'POST';
10753 if(o.clientValidation === false || this.form.isValid()){
10755 if (this.form.progressUrl) {
10756 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10757 (new Date() * 1) + '' + Math.random());
10762 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10763 form:this.form.el.dom,
10764 url:this.getUrl(!isPost),
10766 params:isPost ? this.getParams() : null,
10767 isUpload: this.form.fileUpload,
10768 formData : this.form.formData
10771 this.uploadProgress();
10773 }else if (o.clientValidation !== false){ // client validation failed
10774 this.failureType = Roo.form.Action.CLIENT_INVALID;
10775 this.form.afterAction(this, false);
10779 success : function(response)
10781 this.uploadComplete= true;
10782 if (this.haveProgress) {
10783 Roo.MessageBox.hide();
10787 var result = this.processResponse(response);
10788 if(result === true || result.success){
10789 this.form.afterAction(this, true);
10793 this.form.markInvalid(result.errors);
10794 this.failureType = Roo.form.Action.SERVER_INVALID;
10796 this.form.afterAction(this, false);
10798 failure : function(response)
10800 this.uploadComplete= true;
10801 if (this.haveProgress) {
10802 Roo.MessageBox.hide();
10805 this.response = response;
10806 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10807 this.form.afterAction(this, false);
10810 handleResponse : function(response){
10811 if(this.form.errorReader){
10812 var rs = this.form.errorReader.read(response);
10815 for(var i = 0, len = rs.records.length; i < len; i++) {
10816 var r = rs.records[i];
10817 errors[i] = r.data;
10820 if(errors.length < 1){
10824 success : rs.success,
10830 ret = Roo.decode(response.responseText);
10834 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10844 Roo.form.Action.Load = function(form, options){
10845 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10846 this.reader = this.form.reader;
10849 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10854 Roo.Ajax.request(Roo.apply(
10855 this.createCallback(), {
10856 method:this.getMethod(),
10857 url:this.getUrl(false),
10858 params:this.getParams()
10862 success : function(response){
10864 var result = this.processResponse(response);
10865 if(result === true || !result.success || !result.data){
10866 this.failureType = Roo.form.Action.LOAD_FAILURE;
10867 this.form.afterAction(this, false);
10870 this.form.clearInvalid();
10871 this.form.setValues(result.data);
10872 this.form.afterAction(this, true);
10875 handleResponse : function(response){
10876 if(this.form.reader){
10877 var rs = this.form.reader.read(response);
10878 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10880 success : rs.success,
10884 return Roo.decode(response.responseText);
10888 Roo.form.Action.ACTION_TYPES = {
10889 'load' : Roo.form.Action.Load,
10890 'submit' : Roo.form.Action.Submit
10899 * @class Roo.bootstrap.Form
10900 * @extends Roo.bootstrap.Component
10901 * Bootstrap Form class
10902 * @cfg {String} method GET | POST (default POST)
10903 * @cfg {String} labelAlign top | left (default top)
10904 * @cfg {String} align left | right - for navbars
10905 * @cfg {Boolean} loadMask load mask when submit (default true)
10909 * Create a new Form
10910 * @param {Object} config The config object
10914 Roo.bootstrap.Form = function(config){
10916 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10918 Roo.bootstrap.Form.popover.apply();
10922 * @event clientvalidation
10923 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10924 * @param {Form} this
10925 * @param {Boolean} valid true if the form has passed client-side validation
10927 clientvalidation: true,
10929 * @event beforeaction
10930 * Fires before any action is performed. Return false to cancel the action.
10931 * @param {Form} this
10932 * @param {Action} action The action to be performed
10934 beforeaction: true,
10936 * @event actionfailed
10937 * Fires when an action fails.
10938 * @param {Form} this
10939 * @param {Action} action The action that failed
10941 actionfailed : true,
10943 * @event actioncomplete
10944 * Fires when an action is completed.
10945 * @param {Form} this
10946 * @param {Action} action The action that completed
10948 actioncomplete : true
10952 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10955 * @cfg {String} method
10956 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10960 * @cfg {String} url
10961 * The URL to use for form actions if one isn't supplied in the action options.
10964 * @cfg {Boolean} fileUpload
10965 * Set to true if this form is a file upload.
10969 * @cfg {Object} baseParams
10970 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10974 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10978 * @cfg {Sting} align (left|right) for navbar forms
10983 activeAction : null,
10986 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10987 * element by passing it or its id or mask the form itself by passing in true.
10990 waitMsgTarget : false,
10995 * @cfg {Boolean} errorMask (true|false) default false
11000 * @cfg {Number} maskOffset Default 100
11005 * @cfg {Boolean} maskBody
11009 getAutoCreate : function(){
11013 method : this.method || 'POST',
11014 id : this.id || Roo.id(),
11017 if (this.parent().xtype.match(/^Nav/)) {
11018 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11022 if (this.labelAlign == 'left' ) {
11023 cfg.cls += ' form-horizontal';
11029 initEvents : function()
11031 this.el.on('submit', this.onSubmit, this);
11032 // this was added as random key presses on the form where triggering form submit.
11033 this.el.on('keypress', function(e) {
11034 if (e.getCharCode() != 13) {
11037 // we might need to allow it for textareas.. and some other items.
11038 // check e.getTarget().
11040 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11044 Roo.log("keypress blocked");
11046 e.preventDefault();
11052 onSubmit : function(e){
11057 * Returns true if client-side validation on the form is successful.
11060 isValid : function(){
11061 var items = this.getItems();
11063 var target = false;
11065 items.each(function(f){
11071 Roo.log('invalid field: ' + f.name);
11075 if(!target && f.el.isVisible(true)){
11081 if(this.errorMask && !valid){
11082 Roo.bootstrap.Form.popover.mask(this, target);
11089 * Returns true if any fields in this form have changed since their original load.
11092 isDirty : function(){
11094 var items = this.getItems();
11095 items.each(function(f){
11105 * Performs a predefined action (submit or load) or custom actions you define on this form.
11106 * @param {String} actionName The name of the action type
11107 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11108 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11109 * accept other config options):
11111 Property Type Description
11112 ---------------- --------------- ----------------------------------------------------------------------------------
11113 url String The url for the action (defaults to the form's url)
11114 method String The form method to use (defaults to the form's method, or POST if not defined)
11115 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11116 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11117 validate the form on the client (defaults to false)
11119 * @return {BasicForm} this
11121 doAction : function(action, options){
11122 if(typeof action == 'string'){
11123 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11125 if(this.fireEvent('beforeaction', this, action) !== false){
11126 this.beforeAction(action);
11127 action.run.defer(100, action);
11133 beforeAction : function(action){
11134 var o = action.options;
11139 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11141 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11144 // not really supported yet.. ??
11146 //if(this.waitMsgTarget === true){
11147 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11148 //}else if(this.waitMsgTarget){
11149 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11150 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11152 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11158 afterAction : function(action, success){
11159 this.activeAction = null;
11160 var o = action.options;
11165 Roo.get(document.body).unmask();
11171 //if(this.waitMsgTarget === true){
11172 // this.el.unmask();
11173 //}else if(this.waitMsgTarget){
11174 // this.waitMsgTarget.unmask();
11176 // Roo.MessageBox.updateProgress(1);
11177 // Roo.MessageBox.hide();
11184 Roo.callback(o.success, o.scope, [this, action]);
11185 this.fireEvent('actioncomplete', this, action);
11189 // failure condition..
11190 // we have a scenario where updates need confirming.
11191 // eg. if a locking scenario exists..
11192 // we look for { errors : { needs_confirm : true }} in the response.
11194 (typeof(action.result) != 'undefined') &&
11195 (typeof(action.result.errors) != 'undefined') &&
11196 (typeof(action.result.errors.needs_confirm) != 'undefined')
11199 Roo.log("not supported yet");
11202 Roo.MessageBox.confirm(
11203 "Change requires confirmation",
11204 action.result.errorMsg,
11209 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11219 Roo.callback(o.failure, o.scope, [this, action]);
11220 // show an error message if no failed handler is set..
11221 if (!this.hasListener('actionfailed')) {
11222 Roo.log("need to add dialog support");
11224 Roo.MessageBox.alert("Error",
11225 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11226 action.result.errorMsg :
11227 "Saving Failed, please check your entries or try again"
11232 this.fireEvent('actionfailed', this, action);
11237 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11238 * @param {String} id The value to search for
11241 findField : function(id){
11242 var items = this.getItems();
11243 var field = items.get(id);
11245 items.each(function(f){
11246 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11253 return field || null;
11256 * Mark fields in this form invalid in bulk.
11257 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11258 * @return {BasicForm} this
11260 markInvalid : function(errors){
11261 if(errors instanceof Array){
11262 for(var i = 0, len = errors.length; i < len; i++){
11263 var fieldError = errors[i];
11264 var f = this.findField(fieldError.id);
11266 f.markInvalid(fieldError.msg);
11272 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11273 field.markInvalid(errors[id]);
11277 //Roo.each(this.childForms || [], function (f) {
11278 // f.markInvalid(errors);
11285 * Set values for fields in this form in bulk.
11286 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11287 * @return {BasicForm} this
11289 setValues : function(values){
11290 if(values instanceof Array){ // array of objects
11291 for(var i = 0, len = values.length; i < len; i++){
11293 var f = this.findField(v.id);
11295 f.setValue(v.value);
11296 if(this.trackResetOnLoad){
11297 f.originalValue = f.getValue();
11301 }else{ // object hash
11304 if(typeof values[id] != 'function' && (field = this.findField(id))){
11306 if (field.setFromData &&
11307 field.valueField &&
11308 field.displayField &&
11309 // combos' with local stores can
11310 // be queried via setValue()
11311 // to set their value..
11312 (field.store && !field.store.isLocal)
11316 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11317 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11318 field.setFromData(sd);
11320 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11322 field.setFromData(values);
11325 field.setValue(values[id]);
11329 if(this.trackResetOnLoad){
11330 field.originalValue = field.getValue();
11336 //Roo.each(this.childForms || [], function (f) {
11337 // f.setValues(values);
11344 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11345 * they are returned as an array.
11346 * @param {Boolean} asString
11349 getValues : function(asString){
11350 //if (this.childForms) {
11351 // copy values from the child forms
11352 // Roo.each(this.childForms, function (f) {
11353 // this.setValues(f.getValues());
11359 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11360 if(asString === true){
11363 return Roo.urlDecode(fs);
11367 * Returns the fields in this form as an object with key/value pairs.
11368 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11371 getFieldValues : function(with_hidden)
11373 var items = this.getItems();
11375 items.each(function(f){
11377 if (!f.getName()) {
11381 var v = f.getValue();
11383 if (f.inputType =='radio') {
11384 if (typeof(ret[f.getName()]) == 'undefined') {
11385 ret[f.getName()] = ''; // empty..
11388 if (!f.el.dom.checked) {
11392 v = f.el.dom.value;
11396 if(f.xtype == 'MoneyField'){
11397 ret[f.currencyName] = f.getCurrency();
11400 // not sure if this supported any more..
11401 if ((typeof(v) == 'object') && f.getRawValue) {
11402 v = f.getRawValue() ; // dates..
11404 // combo boxes where name != hiddenName...
11405 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11406 ret[f.name] = f.getRawValue();
11408 ret[f.getName()] = v;
11415 * Clears all invalid messages in this form.
11416 * @return {BasicForm} this
11418 clearInvalid : function(){
11419 var items = this.getItems();
11421 items.each(function(f){
11429 * Resets this form.
11430 * @return {BasicForm} this
11432 reset : function(){
11433 var items = this.getItems();
11434 items.each(function(f){
11438 Roo.each(this.childForms || [], function (f) {
11446 getItems : function()
11448 var r=new Roo.util.MixedCollection(false, function(o){
11449 return o.id || (o.id = Roo.id());
11451 var iter = function(el) {
11458 Roo.each(el.items,function(e) {
11467 hideFields : function(items)
11469 Roo.each(items, function(i){
11471 var f = this.findField(i);
11482 showFields : function(items)
11484 Roo.each(items, function(i){
11486 var f = this.findField(i);
11499 Roo.apply(Roo.bootstrap.Form, {
11515 intervalID : false,
11521 if(this.isApplied){
11526 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11527 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11528 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11529 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11532 this.maskEl.top.enableDisplayMode("block");
11533 this.maskEl.left.enableDisplayMode("block");
11534 this.maskEl.bottom.enableDisplayMode("block");
11535 this.maskEl.right.enableDisplayMode("block");
11537 this.toolTip = new Roo.bootstrap.Tooltip({
11538 cls : 'roo-form-error-popover',
11540 'left' : ['r-l', [-2,0], 'right'],
11541 'right' : ['l-r', [2,0], 'left'],
11542 'bottom' : ['tl-bl', [0,2], 'top'],
11543 'top' : [ 'bl-tl', [0,-2], 'bottom']
11547 this.toolTip.render(Roo.get(document.body));
11549 this.toolTip.el.enableDisplayMode("block");
11551 Roo.get(document.body).on('click', function(){
11555 Roo.get(document.body).on('touchstart', function(){
11559 this.isApplied = true
11562 mask : function(form, target)
11566 this.target = target;
11568 if(!this.form.errorMask || !target.el){
11572 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11574 Roo.log(scrollable);
11576 var ot = this.target.el.calcOffsetsTo(scrollable);
11578 var scrollTo = ot[1] - this.form.maskOffset;
11580 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11582 scrollable.scrollTo('top', scrollTo);
11584 var box = this.target.el.getBox();
11586 var zIndex = Roo.bootstrap.Modal.zIndex++;
11589 this.maskEl.top.setStyle('position', 'absolute');
11590 this.maskEl.top.setStyle('z-index', zIndex);
11591 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11592 this.maskEl.top.setLeft(0);
11593 this.maskEl.top.setTop(0);
11594 this.maskEl.top.show();
11596 this.maskEl.left.setStyle('position', 'absolute');
11597 this.maskEl.left.setStyle('z-index', zIndex);
11598 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11599 this.maskEl.left.setLeft(0);
11600 this.maskEl.left.setTop(box.y - this.padding);
11601 this.maskEl.left.show();
11603 this.maskEl.bottom.setStyle('position', 'absolute');
11604 this.maskEl.bottom.setStyle('z-index', zIndex);
11605 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11606 this.maskEl.bottom.setLeft(0);
11607 this.maskEl.bottom.setTop(box.bottom + this.padding);
11608 this.maskEl.bottom.show();
11610 this.maskEl.right.setStyle('position', 'absolute');
11611 this.maskEl.right.setStyle('z-index', zIndex);
11612 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11613 this.maskEl.right.setLeft(box.right + this.padding);
11614 this.maskEl.right.setTop(box.y - this.padding);
11615 this.maskEl.right.show();
11617 this.toolTip.bindEl = this.target.el;
11619 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11621 var tip = this.target.blankText;
11623 if(this.target.getValue() !== '' ) {
11625 if (this.target.invalidText.length) {
11626 tip = this.target.invalidText;
11627 } else if (this.target.regexText.length){
11628 tip = this.target.regexText;
11632 this.toolTip.show(tip);
11634 this.intervalID = window.setInterval(function() {
11635 Roo.bootstrap.Form.popover.unmask();
11638 window.onwheel = function(){ return false;};
11640 (function(){ this.isMasked = true; }).defer(500, this);
11644 unmask : function()
11646 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11650 this.maskEl.top.setStyle('position', 'absolute');
11651 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11652 this.maskEl.top.hide();
11654 this.maskEl.left.setStyle('position', 'absolute');
11655 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11656 this.maskEl.left.hide();
11658 this.maskEl.bottom.setStyle('position', 'absolute');
11659 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11660 this.maskEl.bottom.hide();
11662 this.maskEl.right.setStyle('position', 'absolute');
11663 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11664 this.maskEl.right.hide();
11666 this.toolTip.hide();
11668 this.toolTip.el.hide();
11670 window.onwheel = function(){ return true;};
11672 if(this.intervalID){
11673 window.clearInterval(this.intervalID);
11674 this.intervalID = false;
11677 this.isMasked = false;
11687 * Ext JS Library 1.1.1
11688 * Copyright(c) 2006-2007, Ext JS, LLC.
11690 * Originally Released Under LGPL - original licence link has changed is not relivant.
11693 * <script type="text/javascript">
11696 * @class Roo.form.VTypes
11697 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11700 Roo.form.VTypes = function(){
11701 // closure these in so they are only created once.
11702 var alpha = /^[a-zA-Z_]+$/;
11703 var alphanum = /^[a-zA-Z0-9_]+$/;
11704 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11705 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11707 // All these messages and functions are configurable
11710 * The function used to validate email addresses
11711 * @param {String} value The email address
11713 'email' : function(v){
11714 return email.test(v);
11717 * The error text to display when the email validation function returns false
11720 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11722 * The keystroke filter mask to be applied on email input
11725 'emailMask' : /[a-z0-9_\.\-@]/i,
11728 * The function used to validate URLs
11729 * @param {String} value The URL
11731 'url' : function(v){
11732 return url.test(v);
11735 * The error text to display when the url validation function returns false
11738 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11741 * The function used to validate alpha values
11742 * @param {String} value The value
11744 'alpha' : function(v){
11745 return alpha.test(v);
11748 * The error text to display when the alpha validation function returns false
11751 'alphaText' : 'This field should only contain letters and _',
11753 * The keystroke filter mask to be applied on alpha input
11756 'alphaMask' : /[a-z_]/i,
11759 * The function used to validate alphanumeric values
11760 * @param {String} value The value
11762 'alphanum' : function(v){
11763 return alphanum.test(v);
11766 * The error text to display when the alphanumeric validation function returns false
11769 'alphanumText' : 'This field should only contain letters, numbers and _',
11771 * The keystroke filter mask to be applied on alphanumeric input
11774 'alphanumMask' : /[a-z0-9_]/i
11784 * @class Roo.bootstrap.Input
11785 * @extends Roo.bootstrap.Component
11786 * Bootstrap Input class
11787 * @cfg {Boolean} disabled is it disabled
11788 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11789 * @cfg {String} name name of the input
11790 * @cfg {string} fieldLabel - the label associated
11791 * @cfg {string} placeholder - placeholder to put in text.
11792 * @cfg {string} before - input group add on before
11793 * @cfg {string} after - input group add on after
11794 * @cfg {string} size - (lg|sm) or leave empty..
11795 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11796 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11797 * @cfg {Number} md colspan out of 12 for computer-sized screens
11798 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11799 * @cfg {string} value default value of the input
11800 * @cfg {Number} labelWidth set the width of label
11801 * @cfg {Number} labellg set the width of label (1-12)
11802 * @cfg {Number} labelmd set the width of label (1-12)
11803 * @cfg {Number} labelsm set the width of label (1-12)
11804 * @cfg {Number} labelxs set the width of label (1-12)
11805 * @cfg {String} labelAlign (top|left)
11806 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11807 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11808 * @cfg {String} indicatorpos (left|right) default left
11809 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11810 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11811 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11813 * @cfg {String} align (left|center|right) Default left
11814 * @cfg {Boolean} forceFeedback (true|false) Default false
11817 * Create a new Input
11818 * @param {Object} config The config object
11821 Roo.bootstrap.Input = function(config){
11823 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11828 * Fires when this field receives input focus.
11829 * @param {Roo.form.Field} this
11834 * Fires when this field loses input focus.
11835 * @param {Roo.form.Field} this
11839 * @event specialkey
11840 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11841 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11842 * @param {Roo.form.Field} this
11843 * @param {Roo.EventObject} e The event object
11848 * Fires just before the field blurs if the field value has changed.
11849 * @param {Roo.form.Field} this
11850 * @param {Mixed} newValue The new value
11851 * @param {Mixed} oldValue The original value
11856 * Fires after the field has been marked as invalid.
11857 * @param {Roo.form.Field} this
11858 * @param {String} msg The validation message
11863 * Fires after the field has been validated with no errors.
11864 * @param {Roo.form.Field} this
11869 * Fires after the key up
11870 * @param {Roo.form.Field} this
11871 * @param {Roo.EventObject} e The event Object
11876 * Fires after the user pastes into input
11877 * @param {Roo.form.Field} this
11878 * @param {Roo.EventObject} e The event Object
11884 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11886 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11887 automatic validation (defaults to "keyup").
11889 validationEvent : "keyup",
11891 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11893 validateOnBlur : true,
11895 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11897 validationDelay : 250,
11899 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11901 focusClass : "x-form-focus", // not needed???
11905 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11907 invalidClass : "has-warning",
11910 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11912 validClass : "has-success",
11915 * @cfg {Boolean} hasFeedback (true|false) default true
11917 hasFeedback : true,
11920 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11922 invalidFeedbackClass : "glyphicon-warning-sign",
11925 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11927 validFeedbackClass : "glyphicon-ok",
11930 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11932 selectOnFocus : false,
11935 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11939 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11944 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11946 disableKeyFilter : false,
11949 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11953 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11957 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11959 blankText : "Please complete this mandatory field",
11962 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11966 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11968 maxLength : Number.MAX_VALUE,
11970 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11972 minLengthText : "The minimum length for this field is {0}",
11974 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11976 maxLengthText : "The maximum length for this field is {0}",
11980 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11981 * If available, this function will be called only after the basic validators all return true, and will be passed the
11982 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11986 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11987 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11988 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11992 * @cfg {String} regexText -- Depricated - use Invalid Text
11997 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12003 autocomplete: false,
12007 inputType : 'text',
12010 placeholder: false,
12015 preventMark: false,
12016 isFormField : true,
12019 labelAlign : false,
12022 formatedValue : false,
12023 forceFeedback : false,
12025 indicatorpos : 'left',
12035 parentLabelAlign : function()
12038 while (parent.parent()) {
12039 parent = parent.parent();
12040 if (typeof(parent.labelAlign) !='undefined') {
12041 return parent.labelAlign;
12048 getAutoCreate : function()
12050 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12056 if(this.inputType != 'hidden'){
12057 cfg.cls = 'form-group' //input-group
12063 type : this.inputType,
12064 value : this.value,
12065 cls : 'form-control',
12066 placeholder : this.placeholder || '',
12067 autocomplete : this.autocomplete || 'new-password'
12069 if (this.inputType == 'file') {
12070 input.style = 'overflow:hidden'; // why not in CSS?
12073 if(this.capture.length){
12074 input.capture = this.capture;
12077 if(this.accept.length){
12078 input.accept = this.accept + "/*";
12082 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12085 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12086 input.maxLength = this.maxLength;
12089 if (this.disabled) {
12090 input.disabled=true;
12093 if (this.readOnly) {
12094 input.readonly=true;
12098 input.name = this.name;
12102 input.cls += ' input-' + this.size;
12106 ['xs','sm','md','lg'].map(function(size){
12107 if (settings[size]) {
12108 cfg.cls += ' col-' + size + '-' + settings[size];
12112 var inputblock = input;
12116 cls: 'glyphicon form-control-feedback'
12119 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12122 cls : 'has-feedback',
12130 if (this.before || this.after) {
12133 cls : 'input-group',
12137 if (this.before && typeof(this.before) == 'string') {
12139 inputblock.cn.push({
12141 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12145 if (this.before && typeof(this.before) == 'object') {
12146 this.before = Roo.factory(this.before);
12148 inputblock.cn.push({
12150 cls : 'roo-input-before input-group-prepend input-group-' +
12151 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12155 inputblock.cn.push(input);
12157 if (this.after && typeof(this.after) == 'string') {
12158 inputblock.cn.push({
12160 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12164 if (this.after && typeof(this.after) == 'object') {
12165 this.after = Roo.factory(this.after);
12167 inputblock.cn.push({
12169 cls : 'roo-input-after input-group-append input-group-' +
12170 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12174 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12175 inputblock.cls += ' has-feedback';
12176 inputblock.cn.push(feedback);
12181 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12182 tooltip : 'This field is required'
12184 if (this.allowBlank ) {
12185 indicator.style = this.allowBlank ? ' display:none' : '';
12187 if (align ==='left' && this.fieldLabel.length) {
12189 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12196 cls : 'control-label col-form-label',
12197 html : this.fieldLabel
12208 var labelCfg = cfg.cn[1];
12209 var contentCfg = cfg.cn[2];
12211 if(this.indicatorpos == 'right'){
12216 cls : 'control-label col-form-label',
12220 html : this.fieldLabel
12234 labelCfg = cfg.cn[0];
12235 contentCfg = cfg.cn[1];
12239 if(this.labelWidth > 12){
12240 labelCfg.style = "width: " + this.labelWidth + 'px';
12243 if(this.labelWidth < 13 && this.labelmd == 0){
12244 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12247 if(this.labellg > 0){
12248 labelCfg.cls += ' col-lg-' + this.labellg;
12249 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12252 if(this.labelmd > 0){
12253 labelCfg.cls += ' col-md-' + this.labelmd;
12254 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12257 if(this.labelsm > 0){
12258 labelCfg.cls += ' col-sm-' + this.labelsm;
12259 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12262 if(this.labelxs > 0){
12263 labelCfg.cls += ' col-xs-' + this.labelxs;
12264 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12268 } else if ( this.fieldLabel.length) {
12275 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12276 tooltip : 'This field is required',
12277 style : this.allowBlank ? ' display:none' : ''
12281 //cls : 'input-group-addon',
12282 html : this.fieldLabel
12290 if(this.indicatorpos == 'right'){
12295 //cls : 'input-group-addon',
12296 html : this.fieldLabel
12301 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12302 tooltip : 'This field is required',
12303 style : this.allowBlank ? ' display:none' : ''
12323 if (this.parentType === 'Navbar' && this.parent().bar) {
12324 cfg.cls += ' navbar-form';
12327 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12328 // on BS4 we do this only if not form
12329 cfg.cls += ' navbar-form';
12337 * return the real input element.
12339 inputEl: function ()
12341 return this.el.select('input.form-control',true).first();
12344 tooltipEl : function()
12346 return this.inputEl();
12349 indicatorEl : function()
12351 if (Roo.bootstrap.version == 4) {
12352 return false; // not enabled in v4 yet.
12355 var indicator = this.el.select('i.roo-required-indicator',true).first();
12365 setDisabled : function(v)
12367 var i = this.inputEl().dom;
12369 i.removeAttribute('disabled');
12373 i.setAttribute('disabled','true');
12375 initEvents : function()
12378 this.inputEl().on("keydown" , this.fireKey, this);
12379 this.inputEl().on("focus", this.onFocus, this);
12380 this.inputEl().on("blur", this.onBlur, this);
12382 this.inputEl().relayEvent('keyup', this);
12383 this.inputEl().relayEvent('paste', this);
12385 this.indicator = this.indicatorEl();
12387 if(this.indicator){
12388 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12391 // reference to original value for reset
12392 this.originalValue = this.getValue();
12393 //Roo.form.TextField.superclass.initEvents.call(this);
12394 if(this.validationEvent == 'keyup'){
12395 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12396 this.inputEl().on('keyup', this.filterValidation, this);
12398 else if(this.validationEvent !== false){
12399 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12402 if(this.selectOnFocus){
12403 this.on("focus", this.preFocus, this);
12406 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12407 this.inputEl().on("keypress", this.filterKeys, this);
12409 this.inputEl().relayEvent('keypress', this);
12412 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12413 this.el.on("click", this.autoSize, this);
12416 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12417 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12420 if (typeof(this.before) == 'object') {
12421 this.before.render(this.el.select('.roo-input-before',true).first());
12423 if (typeof(this.after) == 'object') {
12424 this.after.render(this.el.select('.roo-input-after',true).first());
12427 this.inputEl().on('change', this.onChange, this);
12430 filterValidation : function(e){
12431 if(!e.isNavKeyPress()){
12432 this.validationTask.delay(this.validationDelay);
12436 * Validates the field value
12437 * @return {Boolean} True if the value is valid, else false
12439 validate : function(){
12440 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12441 if(this.disabled || this.validateValue(this.getRawValue())){
12446 this.markInvalid();
12452 * Validates a value according to the field's validation rules and marks the field as invalid
12453 * if the validation fails
12454 * @param {Mixed} value The value to validate
12455 * @return {Boolean} True if the value is valid, else false
12457 validateValue : function(value)
12459 if(this.getVisibilityEl().hasClass('hidden')){
12463 if(value.length < 1) { // if it's blank
12464 if(this.allowBlank){
12470 if(value.length < this.minLength){
12473 if(value.length > this.maxLength){
12477 var vt = Roo.form.VTypes;
12478 if(!vt[this.vtype](value, this)){
12482 if(typeof this.validator == "function"){
12483 var msg = this.validator(value);
12487 if (typeof(msg) == 'string') {
12488 this.invalidText = msg;
12492 if(this.regex && !this.regex.test(value)){
12500 fireKey : function(e){
12501 //Roo.log('field ' + e.getKey());
12502 if(e.isNavKeyPress()){
12503 this.fireEvent("specialkey", this, e);
12506 focus : function (selectText){
12508 this.inputEl().focus();
12509 if(selectText === true){
12510 this.inputEl().dom.select();
12516 onFocus : function(){
12517 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12518 // this.el.addClass(this.focusClass);
12520 if(!this.hasFocus){
12521 this.hasFocus = true;
12522 this.startValue = this.getValue();
12523 this.fireEvent("focus", this);
12527 beforeBlur : Roo.emptyFn,
12531 onBlur : function(){
12533 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12534 //this.el.removeClass(this.focusClass);
12536 this.hasFocus = false;
12537 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12540 var v = this.getValue();
12541 if(String(v) !== String(this.startValue)){
12542 this.fireEvent('change', this, v, this.startValue);
12544 this.fireEvent("blur", this);
12547 onChange : function(e)
12549 var v = this.getValue();
12550 if(String(v) !== String(this.startValue)){
12551 this.fireEvent('change', this, v, this.startValue);
12557 * Resets the current field value to the originally loaded value and clears any validation messages
12559 reset : function(){
12560 this.setValue(this.originalValue);
12564 * Returns the name of the field
12565 * @return {Mixed} name The name field
12567 getName: function(){
12571 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12572 * @return {Mixed} value The field value
12574 getValue : function(){
12576 var v = this.inputEl().getValue();
12581 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12582 * @return {Mixed} value The field value
12584 getRawValue : function(){
12585 var v = this.inputEl().getValue();
12591 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12592 * @param {Mixed} value The value to set
12594 setRawValue : function(v){
12595 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12598 selectText : function(start, end){
12599 var v = this.getRawValue();
12601 start = start === undefined ? 0 : start;
12602 end = end === undefined ? v.length : end;
12603 var d = this.inputEl().dom;
12604 if(d.setSelectionRange){
12605 d.setSelectionRange(start, end);
12606 }else if(d.createTextRange){
12607 var range = d.createTextRange();
12608 range.moveStart("character", start);
12609 range.moveEnd("character", v.length-end);
12616 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12617 * @param {Mixed} value The value to set
12619 setValue : function(v){
12622 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12628 processValue : function(value){
12629 if(this.stripCharsRe){
12630 var newValue = value.replace(this.stripCharsRe, '');
12631 if(newValue !== value){
12632 this.setRawValue(newValue);
12639 preFocus : function(){
12641 if(this.selectOnFocus){
12642 this.inputEl().dom.select();
12645 filterKeys : function(e){
12646 var k = e.getKey();
12647 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12650 var c = e.getCharCode(), cc = String.fromCharCode(c);
12651 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12654 if(!this.maskRe.test(cc)){
12659 * Clear any invalid styles/messages for this field
12661 clearInvalid : function(){
12663 if(!this.el || this.preventMark){ // not rendered
12668 this.el.removeClass([this.invalidClass, 'is-invalid']);
12670 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12672 var feedback = this.el.select('.form-control-feedback', true).first();
12675 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12680 if(this.indicator){
12681 this.indicator.removeClass('visible');
12682 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12685 this.fireEvent('valid', this);
12689 * Mark this field as valid
12691 markValid : function()
12693 if(!this.el || this.preventMark){ // not rendered...
12697 this.el.removeClass([this.invalidClass, this.validClass]);
12698 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12700 var feedback = this.el.select('.form-control-feedback', true).first();
12703 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12706 if(this.indicator){
12707 this.indicator.removeClass('visible');
12708 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12716 if(this.allowBlank && !this.getRawValue().length){
12719 if (Roo.bootstrap.version == 3) {
12720 this.el.addClass(this.validClass);
12722 this.inputEl().addClass('is-valid');
12725 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12727 var feedback = this.el.select('.form-control-feedback', true).first();
12730 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12731 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12736 this.fireEvent('valid', this);
12740 * Mark this field as invalid
12741 * @param {String} msg The validation message
12743 markInvalid : function(msg)
12745 if(!this.el || this.preventMark){ // not rendered
12749 this.el.removeClass([this.invalidClass, this.validClass]);
12750 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12752 var feedback = this.el.select('.form-control-feedback', true).first();
12755 this.el.select('.form-control-feedback', true).first().removeClass(
12756 [this.invalidFeedbackClass, this.validFeedbackClass]);
12763 if(this.allowBlank && !this.getRawValue().length){
12767 if(this.indicator){
12768 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12769 this.indicator.addClass('visible');
12771 if (Roo.bootstrap.version == 3) {
12772 this.el.addClass(this.invalidClass);
12774 this.inputEl().addClass('is-invalid');
12779 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12781 var feedback = this.el.select('.form-control-feedback', true).first();
12784 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12786 if(this.getValue().length || this.forceFeedback){
12787 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12794 this.fireEvent('invalid', this, msg);
12797 SafariOnKeyDown : function(event)
12799 // this is a workaround for a password hang bug on chrome/ webkit.
12800 if (this.inputEl().dom.type != 'password') {
12804 var isSelectAll = false;
12806 if(this.inputEl().dom.selectionEnd > 0){
12807 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12809 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12810 event.preventDefault();
12815 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12817 event.preventDefault();
12818 // this is very hacky as keydown always get's upper case.
12820 var cc = String.fromCharCode(event.getCharCode());
12821 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12825 adjustWidth : function(tag, w){
12826 tag = tag.toLowerCase();
12827 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12828 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12829 if(tag == 'input'){
12832 if(tag == 'textarea'){
12835 }else if(Roo.isOpera){
12836 if(tag == 'input'){
12839 if(tag == 'textarea'){
12847 setFieldLabel : function(v)
12849 if(!this.rendered){
12853 if(this.indicatorEl()){
12854 var ar = this.el.select('label > span',true);
12856 if (ar.elements.length) {
12857 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12858 this.fieldLabel = v;
12862 var br = this.el.select('label',true);
12864 if(br.elements.length) {
12865 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12866 this.fieldLabel = v;
12870 Roo.log('Cannot Found any of label > span || label in input');
12874 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12875 this.fieldLabel = v;
12890 * @class Roo.bootstrap.TextArea
12891 * @extends Roo.bootstrap.Input
12892 * Bootstrap TextArea class
12893 * @cfg {Number} cols Specifies the visible width of a text area
12894 * @cfg {Number} rows Specifies the visible number of lines in a text area
12895 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12896 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12897 * @cfg {string} html text
12900 * Create a new TextArea
12901 * @param {Object} config The config object
12904 Roo.bootstrap.TextArea = function(config){
12905 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12909 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12919 getAutoCreate : function(){
12921 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12927 if(this.inputType != 'hidden'){
12928 cfg.cls = 'form-group' //input-group
12936 value : this.value || '',
12937 html: this.html || '',
12938 cls : 'form-control',
12939 placeholder : this.placeholder || ''
12943 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12944 input.maxLength = this.maxLength;
12948 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12952 input.cols = this.cols;
12955 if (this.readOnly) {
12956 input.readonly = true;
12960 input.name = this.name;
12964 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12968 ['xs','sm','md','lg'].map(function(size){
12969 if (settings[size]) {
12970 cfg.cls += ' col-' + size + '-' + settings[size];
12974 var inputblock = input;
12976 if(this.hasFeedback && !this.allowBlank){
12980 cls: 'glyphicon form-control-feedback'
12984 cls : 'has-feedback',
12993 if (this.before || this.after) {
12996 cls : 'input-group',
13000 inputblock.cn.push({
13002 cls : 'input-group-addon',
13007 inputblock.cn.push(input);
13009 if(this.hasFeedback && !this.allowBlank){
13010 inputblock.cls += ' has-feedback';
13011 inputblock.cn.push(feedback);
13015 inputblock.cn.push({
13017 cls : 'input-group-addon',
13024 if (align ==='left' && this.fieldLabel.length) {
13029 cls : 'control-label',
13030 html : this.fieldLabel
13041 if(this.labelWidth > 12){
13042 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13045 if(this.labelWidth < 13 && this.labelmd == 0){
13046 this.labelmd = this.labelWidth;
13049 if(this.labellg > 0){
13050 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13051 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13054 if(this.labelmd > 0){
13055 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13056 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13059 if(this.labelsm > 0){
13060 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13061 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13064 if(this.labelxs > 0){
13065 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13066 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13069 } else if ( this.fieldLabel.length) {
13074 //cls : 'input-group-addon',
13075 html : this.fieldLabel
13093 if (this.disabled) {
13094 input.disabled=true;
13101 * return the real textarea element.
13103 inputEl: function ()
13105 return this.el.select('textarea.form-control',true).first();
13109 * Clear any invalid styles/messages for this field
13111 clearInvalid : function()
13114 if(!this.el || this.preventMark){ // not rendered
13118 var label = this.el.select('label', true).first();
13119 var icon = this.el.select('i.fa-star', true).first();
13124 this.el.removeClass( this.validClass);
13125 this.inputEl().removeClass('is-invalid');
13127 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13129 var feedback = this.el.select('.form-control-feedback', true).first();
13132 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13137 this.fireEvent('valid', this);
13141 * Mark this field as valid
13143 markValid : function()
13145 if(!this.el || this.preventMark){ // not rendered
13149 this.el.removeClass([this.invalidClass, this.validClass]);
13150 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13152 var feedback = this.el.select('.form-control-feedback', true).first();
13155 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13158 if(this.disabled || this.allowBlank){
13162 var label = this.el.select('label', true).first();
13163 var icon = this.el.select('i.fa-star', true).first();
13168 if (Roo.bootstrap.version == 3) {
13169 this.el.addClass(this.validClass);
13171 this.inputEl().addClass('is-valid');
13175 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13177 var feedback = this.el.select('.form-control-feedback', true).first();
13180 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13181 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13186 this.fireEvent('valid', this);
13190 * Mark this field as invalid
13191 * @param {String} msg The validation message
13193 markInvalid : function(msg)
13195 if(!this.el || this.preventMark){ // not rendered
13199 this.el.removeClass([this.invalidClass, this.validClass]);
13200 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13202 var feedback = this.el.select('.form-control-feedback', true).first();
13205 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13208 if(this.disabled || this.allowBlank){
13212 var label = this.el.select('label', true).first();
13213 var icon = this.el.select('i.fa-star', true).first();
13215 if(!this.getValue().length && label && !icon){
13216 this.el.createChild({
13218 cls : 'text-danger fa fa-lg fa-star',
13219 tooltip : 'This field is required',
13220 style : 'margin-right:5px;'
13224 if (Roo.bootstrap.version == 3) {
13225 this.el.addClass(this.invalidClass);
13227 this.inputEl().addClass('is-invalid');
13230 // fixme ... this may be depricated need to test..
13231 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13233 var feedback = this.el.select('.form-control-feedback', true).first();
13236 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13238 if(this.getValue().length || this.forceFeedback){
13239 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13246 this.fireEvent('invalid', this, msg);
13254 * trigger field - base class for combo..
13259 * @class Roo.bootstrap.TriggerField
13260 * @extends Roo.bootstrap.Input
13261 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13262 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13263 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13264 * for which you can provide a custom implementation. For example:
13266 var trigger = new Roo.bootstrap.TriggerField();
13267 trigger.onTriggerClick = myTriggerFn;
13268 trigger.applyTo('my-field');
13271 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13272 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13273 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13274 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13275 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13278 * Create a new TriggerField.
13279 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13280 * to the base TextField)
13282 Roo.bootstrap.TriggerField = function(config){
13283 this.mimicing = false;
13284 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13287 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13289 * @cfg {String} triggerClass A CSS class to apply to the trigger
13292 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13297 * @cfg {Boolean} removable (true|false) special filter default false
13301 /** @cfg {Boolean} grow @hide */
13302 /** @cfg {Number} growMin @hide */
13303 /** @cfg {Number} growMax @hide */
13309 autoSize: Roo.emptyFn,
13313 deferHeight : true,
13316 actionMode : 'wrap',
13321 getAutoCreate : function(){
13323 var align = this.labelAlign || this.parentLabelAlign();
13328 cls: 'form-group' //input-group
13335 type : this.inputType,
13336 cls : 'form-control',
13337 autocomplete: 'new-password',
13338 placeholder : this.placeholder || ''
13342 input.name = this.name;
13345 input.cls += ' input-' + this.size;
13348 if (this.disabled) {
13349 input.disabled=true;
13352 var inputblock = input;
13354 if(this.hasFeedback && !this.allowBlank){
13358 cls: 'glyphicon form-control-feedback'
13361 if(this.removable && !this.editable ){
13363 cls : 'has-feedback',
13369 cls : 'roo-combo-removable-btn close'
13376 cls : 'has-feedback',
13385 if(this.removable && !this.editable ){
13387 cls : 'roo-removable',
13393 cls : 'roo-combo-removable-btn close'
13400 if (this.before || this.after) {
13403 cls : 'input-group',
13407 inputblock.cn.push({
13409 cls : 'input-group-addon input-group-prepend input-group-text',
13414 inputblock.cn.push(input);
13416 if(this.hasFeedback && !this.allowBlank){
13417 inputblock.cls += ' has-feedback';
13418 inputblock.cn.push(feedback);
13422 inputblock.cn.push({
13424 cls : 'input-group-addon input-group-append input-group-text',
13433 var ibwrap = inputblock;
13438 cls: 'roo-select2-choices',
13442 cls: 'roo-select2-search-field',
13454 cls: 'roo-select2-container input-group',
13459 cls: 'form-hidden-field'
13465 if(!this.multiple && this.showToggleBtn){
13471 if (this.caret != false) {
13474 cls: 'fa fa-' + this.caret
13481 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13483 Roo.bootstrap.version == 3 ? caret : '',
13486 cls: 'combobox-clear',
13500 combobox.cls += ' roo-select2-container-multi';
13504 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13505 tooltip : 'This field is required'
13507 if (Roo.bootstrap.version == 4) {
13510 style : 'display:none'
13515 if (align ==='left' && this.fieldLabel.length) {
13517 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13524 cls : 'control-label',
13525 html : this.fieldLabel
13537 var labelCfg = cfg.cn[1];
13538 var contentCfg = cfg.cn[2];
13540 if(this.indicatorpos == 'right'){
13545 cls : 'control-label',
13549 html : this.fieldLabel
13563 labelCfg = cfg.cn[0];
13564 contentCfg = cfg.cn[1];
13567 if(this.labelWidth > 12){
13568 labelCfg.style = "width: " + this.labelWidth + 'px';
13571 if(this.labelWidth < 13 && this.labelmd == 0){
13572 this.labelmd = this.labelWidth;
13575 if(this.labellg > 0){
13576 labelCfg.cls += ' col-lg-' + this.labellg;
13577 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13580 if(this.labelmd > 0){
13581 labelCfg.cls += ' col-md-' + this.labelmd;
13582 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13585 if(this.labelsm > 0){
13586 labelCfg.cls += ' col-sm-' + this.labelsm;
13587 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13590 if(this.labelxs > 0){
13591 labelCfg.cls += ' col-xs-' + this.labelxs;
13592 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13595 } else if ( this.fieldLabel.length) {
13596 // Roo.log(" label");
13601 //cls : 'input-group-addon',
13602 html : this.fieldLabel
13610 if(this.indicatorpos == 'right'){
13618 html : this.fieldLabel
13632 // Roo.log(" no label && no align");
13639 ['xs','sm','md','lg'].map(function(size){
13640 if (settings[size]) {
13641 cfg.cls += ' col-' + size + '-' + settings[size];
13652 onResize : function(w, h){
13653 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13654 // if(typeof w == 'number'){
13655 // var x = w - this.trigger.getWidth();
13656 // this.inputEl().setWidth(this.adjustWidth('input', x));
13657 // this.trigger.setStyle('left', x+'px');
13662 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13665 getResizeEl : function(){
13666 return this.inputEl();
13670 getPositionEl : function(){
13671 return this.inputEl();
13675 alignErrorIcon : function(){
13676 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13680 initEvents : function(){
13684 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13685 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13686 if(!this.multiple && this.showToggleBtn){
13687 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13688 if(this.hideTrigger){
13689 this.trigger.setDisplayed(false);
13691 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13695 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13698 if(this.removable && !this.editable && !this.tickable){
13699 var close = this.closeTriggerEl();
13702 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13703 close.on('click', this.removeBtnClick, this, close);
13707 //this.trigger.addClassOnOver('x-form-trigger-over');
13708 //this.trigger.addClassOnClick('x-form-trigger-click');
13711 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13715 closeTriggerEl : function()
13717 var close = this.el.select('.roo-combo-removable-btn', true).first();
13718 return close ? close : false;
13721 removeBtnClick : function(e, h, el)
13723 e.preventDefault();
13725 if(this.fireEvent("remove", this) !== false){
13727 this.fireEvent("afterremove", this)
13731 createList : function()
13733 this.list = Roo.get(document.body).createChild({
13734 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13735 cls: 'typeahead typeahead-long dropdown-menu shadow',
13736 style: 'display:none'
13739 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13744 initTrigger : function(){
13749 onDestroy : function(){
13751 this.trigger.removeAllListeners();
13752 // this.trigger.remove();
13755 // this.wrap.remove();
13757 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13761 onFocus : function(){
13762 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13764 if(!this.mimicing){
13765 this.wrap.addClass('x-trigger-wrap-focus');
13766 this.mimicing = true;
13767 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13768 if(this.monitorTab){
13769 this.el.on("keydown", this.checkTab, this);
13776 checkTab : function(e){
13777 if(e.getKey() == e.TAB){
13778 this.triggerBlur();
13783 onBlur : function(){
13788 mimicBlur : function(e, t){
13790 if(!this.wrap.contains(t) && this.validateBlur()){
13791 this.triggerBlur();
13797 triggerBlur : function(){
13798 this.mimicing = false;
13799 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13800 if(this.monitorTab){
13801 this.el.un("keydown", this.checkTab, this);
13803 //this.wrap.removeClass('x-trigger-wrap-focus');
13804 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13808 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13809 validateBlur : function(e, t){
13814 onDisable : function(){
13815 this.inputEl().dom.disabled = true;
13816 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13818 // this.wrap.addClass('x-item-disabled');
13823 onEnable : function(){
13824 this.inputEl().dom.disabled = false;
13825 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13827 // this.el.removeClass('x-item-disabled');
13832 onShow : function(){
13833 var ae = this.getActionEl();
13836 ae.dom.style.display = '';
13837 ae.dom.style.visibility = 'visible';
13843 onHide : function(){
13844 var ae = this.getActionEl();
13845 ae.dom.style.display = 'none';
13849 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13850 * by an implementing function.
13852 * @param {EventObject} e
13854 onTriggerClick : Roo.emptyFn
13862 * @class Roo.bootstrap.CardUploader
13863 * @extends Roo.bootstrap.Button
13864 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13865 * @cfg {Number} errorTimeout default 3000
13866 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13867 * @cfg {Array} html The button text.
13871 * Create a new CardUploader
13872 * @param {Object} config The config object
13875 Roo.bootstrap.CardUploader = function(config){
13879 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13882 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13890 * When a image is clicked on - and needs to display a slideshow or similar..
13891 * @param {Roo.bootstrap.Card} this
13892 * @param {Object} The image information data
13898 * When a the download link is clicked
13899 * @param {Roo.bootstrap.Card} this
13900 * @param {Object} The image information data contains
13907 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13910 errorTimeout : 3000,
13914 fileCollection : false,
13917 getAutoCreate : function()
13921 cls :'form-group' ,
13926 //cls : 'input-group-addon',
13927 html : this.fieldLabel
13935 value : this.value,
13936 cls : 'd-none form-control'
13941 multiple : 'multiple',
13943 cls : 'd-none roo-card-upload-selector'
13947 cls : 'roo-card-uploader-button-container w-100 mb-2'
13950 cls : 'card-columns roo-card-uploader-container'
13960 getChildContainer : function() /// what children are added to.
13962 return this.containerEl;
13965 getButtonContainer : function() /// what children are added to.
13967 return this.el.select(".roo-card-uploader-button-container").first();
13970 initEvents : function()
13973 Roo.bootstrap.Input.prototype.initEvents.call(this);
13977 xns: Roo.bootstrap,
13980 container_method : 'getButtonContainer' ,
13981 html : this.html, // fix changable?
13984 'click' : function(btn, e) {
13993 this.urlAPI = (window.createObjectURL && window) ||
13994 (window.URL && URL.revokeObjectURL && URL) ||
13995 (window.webkitURL && webkitURL);
14000 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14002 this.selectorEl.on('change', this.onFileSelected, this);
14005 this.images.forEach(function(img) {
14008 this.images = false;
14010 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14016 onClick : function(e)
14018 e.preventDefault();
14020 this.selectorEl.dom.click();
14024 onFileSelected : function(e)
14026 e.preventDefault();
14028 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14032 Roo.each(this.selectorEl.dom.files, function(file){
14033 this.addFile(file);
14042 addFile : function(file)
14045 if(typeof(file) === 'string'){
14046 throw "Add file by name?"; // should not happen
14050 if(!file || !this.urlAPI){
14060 var url = _this.urlAPI.createObjectURL( file);
14063 id : Roo.bootstrap.CardUploader.ID--,
14064 is_uploaded : false,
14068 mimetype : file.type,
14076 * addCard - add an Attachment to the uploader
14077 * @param data - the data about the image to upload
14081 title : "Title of file",
14082 is_uploaded : false,
14083 src : "http://.....",
14084 srcfile : { the File upload object },
14085 mimetype : file.type,
14088 .. any other data...
14094 addCard : function (data)
14096 // hidden input element?
14097 // if the file is not an image...
14098 //then we need to use something other that and header_image
14103 xns : Roo.bootstrap,
14104 xtype : 'CardFooter',
14107 xns : Roo.bootstrap,
14113 xns : Roo.bootstrap,
14115 html : String.format("<small>{0}</small>", data.title),
14116 cls : 'col-10 text-left',
14121 click : function() {
14123 t.fireEvent( "download", t, data );
14129 xns : Roo.bootstrap,
14131 style: 'max-height: 28px; ',
14137 click : function() {
14138 t.removeCard(data.id)
14150 var cn = this.addxtype(
14153 xns : Roo.bootstrap,
14156 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14157 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14158 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14163 initEvents : function() {
14164 Roo.bootstrap.Card.prototype.initEvents.call(this);
14166 this.imgEl = this.el.select('.card-img-top').first();
14168 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14169 this.imgEl.set({ 'pointer' : 'cursor' });
14172 this.getCardFooter().addClass('p-1');
14179 // dont' really need ot update items.
14180 // this.items.push(cn);
14181 this.fileCollection.add(cn);
14183 if (!data.srcfile) {
14184 this.updateInput();
14189 var reader = new FileReader();
14190 reader.addEventListener("load", function() {
14191 data.srcdata = reader.result;
14194 reader.readAsDataURL(data.srcfile);
14199 removeCard : function(id)
14202 var card = this.fileCollection.get(id);
14203 card.data.is_deleted = 1;
14204 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14205 //this.fileCollection.remove(card);
14206 //this.items = this.items.filter(function(e) { return e != card });
14207 // dont' really need ot update items.
14208 card.el.dom.parentNode.removeChild(card.el.dom);
14209 this.updateInput();
14215 this.fileCollection.each(function(card) {
14216 if (card.el.dom && card.el.dom.parentNode) {
14217 card.el.dom.parentNode.removeChild(card.el.dom);
14220 this.fileCollection.clear();
14221 this.updateInput();
14224 updateInput : function()
14227 this.fileCollection.each(function(e) {
14231 this.inputEl().dom.value = JSON.stringify(data);
14241 Roo.bootstrap.CardUploader.ID = -1;/*
14243 * Ext JS Library 1.1.1
14244 * Copyright(c) 2006-2007, Ext JS, LLC.
14246 * Originally Released Under LGPL - original licence link has changed is not relivant.
14249 * <script type="text/javascript">
14254 * @class Roo.data.SortTypes
14256 * Defines the default sorting (casting?) comparison functions used when sorting data.
14258 Roo.data.SortTypes = {
14260 * Default sort that does nothing
14261 * @param {Mixed} s The value being converted
14262 * @return {Mixed} The comparison value
14264 none : function(s){
14269 * The regular expression used to strip tags
14273 stripTagsRE : /<\/?[^>]+>/gi,
14276 * Strips all HTML tags to sort on text only
14277 * @param {Mixed} s The value being converted
14278 * @return {String} The comparison value
14280 asText : function(s){
14281 return String(s).replace(this.stripTagsRE, "");
14285 * Strips all HTML tags to sort on text only - Case insensitive
14286 * @param {Mixed} s The value being converted
14287 * @return {String} The comparison value
14289 asUCText : function(s){
14290 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14294 * Case insensitive string
14295 * @param {Mixed} s The value being converted
14296 * @return {String} The comparison value
14298 asUCString : function(s) {
14299 return String(s).toUpperCase();
14304 * @param {Mixed} s The value being converted
14305 * @return {Number} The comparison value
14307 asDate : function(s) {
14311 if(s instanceof Date){
14312 return s.getTime();
14314 return Date.parse(String(s));
14319 * @param {Mixed} s The value being converted
14320 * @return {Float} The comparison value
14322 asFloat : function(s) {
14323 var val = parseFloat(String(s).replace(/,/g, ""));
14332 * @param {Mixed} s The value being converted
14333 * @return {Number} The comparison value
14335 asInt : function(s) {
14336 var val = parseInt(String(s).replace(/,/g, ""));
14344 * Ext JS Library 1.1.1
14345 * Copyright(c) 2006-2007, Ext JS, LLC.
14347 * Originally Released Under LGPL - original licence link has changed is not relivant.
14350 * <script type="text/javascript">
14354 * @class Roo.data.Record
14355 * Instances of this class encapsulate both record <em>definition</em> information, and record
14356 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14357 * to access Records cached in an {@link Roo.data.Store} object.<br>
14359 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14360 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14363 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14365 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14366 * {@link #create}. The parameters are the same.
14367 * @param {Array} data An associative Array of data values keyed by the field name.
14368 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14369 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14370 * not specified an integer id is generated.
14372 Roo.data.Record = function(data, id){
14373 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14378 * Generate a constructor for a specific record layout.
14379 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14380 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14381 * Each field definition object may contain the following properties: <ul>
14382 * <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,
14383 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14384 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14385 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14386 * is being used, then this is a string containing the javascript expression to reference the data relative to
14387 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14388 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14389 * this may be omitted.</p></li>
14390 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14391 * <ul><li>auto (Default, implies no conversion)</li>
14396 * <li>date</li></ul></p></li>
14397 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14398 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14399 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14400 * by the Reader into an object that will be stored in the Record. It is passed the
14401 * following parameters:<ul>
14402 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14404 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14406 * <br>usage:<br><pre><code>
14407 var TopicRecord = Roo.data.Record.create(
14408 {name: 'title', mapping: 'topic_title'},
14409 {name: 'author', mapping: 'username'},
14410 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14411 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14412 {name: 'lastPoster', mapping: 'user2'},
14413 {name: 'excerpt', mapping: 'post_text'}
14416 var myNewRecord = new TopicRecord({
14417 title: 'Do my job please',
14420 lastPost: new Date(),
14421 lastPoster: 'Animal',
14422 excerpt: 'No way dude!'
14424 myStore.add(myNewRecord);
14429 Roo.data.Record.create = function(o){
14430 var f = function(){
14431 f.superclass.constructor.apply(this, arguments);
14433 Roo.extend(f, Roo.data.Record);
14434 var p = f.prototype;
14435 p.fields = new Roo.util.MixedCollection(false, function(field){
14438 for(var i = 0, len = o.length; i < len; i++){
14439 p.fields.add(new Roo.data.Field(o[i]));
14441 f.getField = function(name){
14442 return p.fields.get(name);
14447 Roo.data.Record.AUTO_ID = 1000;
14448 Roo.data.Record.EDIT = 'edit';
14449 Roo.data.Record.REJECT = 'reject';
14450 Roo.data.Record.COMMIT = 'commit';
14452 Roo.data.Record.prototype = {
14454 * Readonly flag - true if this record has been modified.
14463 join : function(store){
14464 this.store = store;
14468 * Set the named field to the specified value.
14469 * @param {String} name The name of the field to set.
14470 * @param {Object} value The value to set the field to.
14472 set : function(name, value){
14473 if(this.data[name] == value){
14477 if(!this.modified){
14478 this.modified = {};
14480 if(typeof this.modified[name] == 'undefined'){
14481 this.modified[name] = this.data[name];
14483 this.data[name] = value;
14484 if(!this.editing && this.store){
14485 this.store.afterEdit(this);
14490 * Get the value of the named field.
14491 * @param {String} name The name of the field to get the value of.
14492 * @return {Object} The value of the field.
14494 get : function(name){
14495 return this.data[name];
14499 beginEdit : function(){
14500 this.editing = true;
14501 this.modified = {};
14505 cancelEdit : function(){
14506 this.editing = false;
14507 delete this.modified;
14511 endEdit : function(){
14512 this.editing = false;
14513 if(this.dirty && this.store){
14514 this.store.afterEdit(this);
14519 * Usually called by the {@link Roo.data.Store} which owns the Record.
14520 * Rejects all changes made to the Record since either creation, or the last commit operation.
14521 * Modified fields are reverted to their original values.
14523 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14524 * of reject operations.
14526 reject : function(){
14527 var m = this.modified;
14529 if(typeof m[n] != "function"){
14530 this.data[n] = m[n];
14533 this.dirty = false;
14534 delete this.modified;
14535 this.editing = false;
14537 this.store.afterReject(this);
14542 * Usually called by the {@link Roo.data.Store} which owns the Record.
14543 * Commits all changes made to the Record since either creation, or the last commit operation.
14545 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14546 * of commit operations.
14548 commit : function(){
14549 this.dirty = false;
14550 delete this.modified;
14551 this.editing = false;
14553 this.store.afterCommit(this);
14558 hasError : function(){
14559 return this.error != null;
14563 clearError : function(){
14568 * Creates a copy of this record.
14569 * @param {String} id (optional) A new record id if you don't want to use this record's id
14572 copy : function(newId) {
14573 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14577 * Ext JS Library 1.1.1
14578 * Copyright(c) 2006-2007, Ext JS, LLC.
14580 * Originally Released Under LGPL - original licence link has changed is not relivant.
14583 * <script type="text/javascript">
14589 * @class Roo.data.Store
14590 * @extends Roo.util.Observable
14591 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14592 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14594 * 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
14595 * has no knowledge of the format of the data returned by the Proxy.<br>
14597 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14598 * instances from the data object. These records are cached and made available through accessor functions.
14600 * Creates a new Store.
14601 * @param {Object} config A config object containing the objects needed for the Store to access data,
14602 * and read the data into Records.
14604 Roo.data.Store = function(config){
14605 this.data = new Roo.util.MixedCollection(false);
14606 this.data.getKey = function(o){
14609 this.baseParams = {};
14611 this.paramNames = {
14616 "multisort" : "_multisort"
14619 if(config && config.data){
14620 this.inlineData = config.data;
14621 delete config.data;
14624 Roo.apply(this, config);
14626 if(this.reader){ // reader passed
14627 this.reader = Roo.factory(this.reader, Roo.data);
14628 this.reader.xmodule = this.xmodule || false;
14629 if(!this.recordType){
14630 this.recordType = this.reader.recordType;
14632 if(this.reader.onMetaChange){
14633 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14637 if(this.recordType){
14638 this.fields = this.recordType.prototype.fields;
14640 this.modified = [];
14644 * @event datachanged
14645 * Fires when the data cache has changed, and a widget which is using this Store
14646 * as a Record cache should refresh its view.
14647 * @param {Store} this
14649 datachanged : true,
14651 * @event metachange
14652 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14653 * @param {Store} this
14654 * @param {Object} meta The JSON metadata
14659 * Fires when Records have been added to the Store
14660 * @param {Store} this
14661 * @param {Roo.data.Record[]} records The array of Records added
14662 * @param {Number} index The index at which the record(s) were added
14667 * Fires when a Record has been removed from the Store
14668 * @param {Store} this
14669 * @param {Roo.data.Record} record The Record that was removed
14670 * @param {Number} index The index at which the record was removed
14675 * Fires when a Record has been updated
14676 * @param {Store} this
14677 * @param {Roo.data.Record} record The Record that was updated
14678 * @param {String} operation The update operation being performed. Value may be one of:
14680 Roo.data.Record.EDIT
14681 Roo.data.Record.REJECT
14682 Roo.data.Record.COMMIT
14688 * Fires when the data cache has been cleared.
14689 * @param {Store} this
14693 * @event beforeload
14694 * Fires before a request is made for a new data object. If the beforeload handler returns false
14695 * the load action will be canceled.
14696 * @param {Store} this
14697 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14701 * @event beforeloadadd
14702 * Fires after a new set of Records has been loaded.
14703 * @param {Store} this
14704 * @param {Roo.data.Record[]} records The Records that were loaded
14705 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14707 beforeloadadd : true,
14710 * Fires after a new set of Records has been loaded, before they are added to the store.
14711 * @param {Store} this
14712 * @param {Roo.data.Record[]} records The Records that were loaded
14713 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14714 * @params {Object} return from reader
14718 * @event loadexception
14719 * Fires if an exception occurs in the Proxy during loading.
14720 * Called with the signature of the Proxy's "loadexception" event.
14721 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14724 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14725 * @param {Object} load options
14726 * @param {Object} jsonData from your request (normally this contains the Exception)
14728 loadexception : true
14732 this.proxy = Roo.factory(this.proxy, Roo.data);
14733 this.proxy.xmodule = this.xmodule || false;
14734 this.relayEvents(this.proxy, ["loadexception"]);
14736 this.sortToggle = {};
14737 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14739 Roo.data.Store.superclass.constructor.call(this);
14741 if(this.inlineData){
14742 this.loadData(this.inlineData);
14743 delete this.inlineData;
14747 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14749 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14750 * without a remote query - used by combo/forms at present.
14754 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14757 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14760 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14761 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14764 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14765 * on any HTTP request
14768 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14771 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14775 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14776 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14778 remoteSort : false,
14781 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14782 * loaded or when a record is removed. (defaults to false).
14784 pruneModifiedRecords : false,
14787 lastOptions : null,
14790 * Add Records to the Store and fires the add event.
14791 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14793 add : function(records){
14794 records = [].concat(records);
14795 for(var i = 0, len = records.length; i < len; i++){
14796 records[i].join(this);
14798 var index = this.data.length;
14799 this.data.addAll(records);
14800 this.fireEvent("add", this, records, index);
14804 * Remove a Record from the Store and fires the remove event.
14805 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14807 remove : function(record){
14808 var index = this.data.indexOf(record);
14809 this.data.removeAt(index);
14811 if(this.pruneModifiedRecords){
14812 this.modified.remove(record);
14814 this.fireEvent("remove", this, record, index);
14818 * Remove all Records from the Store and fires the clear event.
14820 removeAll : function(){
14822 if(this.pruneModifiedRecords){
14823 this.modified = [];
14825 this.fireEvent("clear", this);
14829 * Inserts Records to the Store at the given index and fires the add event.
14830 * @param {Number} index The start index at which to insert the passed Records.
14831 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14833 insert : function(index, records){
14834 records = [].concat(records);
14835 for(var i = 0, len = records.length; i < len; i++){
14836 this.data.insert(index, records[i]);
14837 records[i].join(this);
14839 this.fireEvent("add", this, records, index);
14843 * Get the index within the cache of the passed Record.
14844 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14845 * @return {Number} The index of the passed Record. Returns -1 if not found.
14847 indexOf : function(record){
14848 return this.data.indexOf(record);
14852 * Get the index within the cache of the Record with the passed id.
14853 * @param {String} id The id of the Record to find.
14854 * @return {Number} The index of the Record. Returns -1 if not found.
14856 indexOfId : function(id){
14857 return this.data.indexOfKey(id);
14861 * Get the Record with the specified id.
14862 * @param {String} id The id of the Record to find.
14863 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14865 getById : function(id){
14866 return this.data.key(id);
14870 * Get the Record at the specified index.
14871 * @param {Number} index The index of the Record to find.
14872 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14874 getAt : function(index){
14875 return this.data.itemAt(index);
14879 * Returns a range of Records between specified indices.
14880 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14881 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14882 * @return {Roo.data.Record[]} An array of Records
14884 getRange : function(start, end){
14885 return this.data.getRange(start, end);
14889 storeOptions : function(o){
14890 o = Roo.apply({}, o);
14893 this.lastOptions = o;
14897 * Loads the Record cache from the configured Proxy using the configured Reader.
14899 * If using remote paging, then the first load call must specify the <em>start</em>
14900 * and <em>limit</em> properties in the options.params property to establish the initial
14901 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14903 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14904 * and this call will return before the new data has been loaded. Perform any post-processing
14905 * in a callback function, or in a "load" event handler.</strong>
14907 * @param {Object} options An object containing properties which control loading options:<ul>
14908 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14909 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14910 * passed the following arguments:<ul>
14911 * <li>r : Roo.data.Record[]</li>
14912 * <li>options: Options object from the load call</li>
14913 * <li>success: Boolean success indicator</li></ul></li>
14914 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14915 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14918 load : function(options){
14919 options = options || {};
14920 if(this.fireEvent("beforeload", this, options) !== false){
14921 this.storeOptions(options);
14922 var p = Roo.apply(options.params || {}, this.baseParams);
14923 // if meta was not loaded from remote source.. try requesting it.
14924 if (!this.reader.metaFromRemote) {
14925 p._requestMeta = 1;
14927 if(this.sortInfo && this.remoteSort){
14928 var pn = this.paramNames;
14929 p[pn["sort"]] = this.sortInfo.field;
14930 p[pn["dir"]] = this.sortInfo.direction;
14932 if (this.multiSort) {
14933 var pn = this.paramNames;
14934 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14937 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14942 * Reloads the Record cache from the configured Proxy using the configured Reader and
14943 * the options from the last load operation performed.
14944 * @param {Object} options (optional) An object containing properties which may override the options
14945 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14946 * the most recently used options are reused).
14948 reload : function(options){
14949 this.load(Roo.applyIf(options||{}, this.lastOptions));
14953 // Called as a callback by the Reader during a load operation.
14954 loadRecords : function(o, options, success){
14955 if(!o || success === false){
14956 if(success !== false){
14957 this.fireEvent("load", this, [], options, o);
14959 if(options.callback){
14960 options.callback.call(options.scope || this, [], options, false);
14964 // if data returned failure - throw an exception.
14965 if (o.success === false) {
14966 // show a message if no listener is registered.
14967 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14968 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14970 // loadmask wil be hooked into this..
14971 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14974 var r = o.records, t = o.totalRecords || r.length;
14976 this.fireEvent("beforeloadadd", this, r, options, o);
14978 if(!options || options.add !== true){
14979 if(this.pruneModifiedRecords){
14980 this.modified = [];
14982 for(var i = 0, len = r.length; i < len; i++){
14986 this.data = this.snapshot;
14987 delete this.snapshot;
14990 this.data.addAll(r);
14991 this.totalLength = t;
14993 this.fireEvent("datachanged", this);
14995 this.totalLength = Math.max(t, this.data.length+r.length);
14999 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15001 var e = new Roo.data.Record({});
15003 e.set(this.parent.displayField, this.parent.emptyTitle);
15004 e.set(this.parent.valueField, '');
15009 this.fireEvent("load", this, r, options, o);
15010 if(options.callback){
15011 options.callback.call(options.scope || this, r, options, true);
15017 * Loads data from a passed data block. A Reader which understands the format of the data
15018 * must have been configured in the constructor.
15019 * @param {Object} data The data block from which to read the Records. The format of the data expected
15020 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15021 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15023 loadData : function(o, append){
15024 var r = this.reader.readRecords(o);
15025 this.loadRecords(r, {add: append}, true);
15029 * using 'cn' the nested child reader read the child array into it's child stores.
15030 * @param {Object} rec The record with a 'children array
15032 loadDataFromChildren : function(rec)
15034 this.loadData(this.reader.toLoadData(rec));
15039 * Gets the number of cached records.
15041 * <em>If using paging, this may not be the total size of the dataset. If the data object
15042 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15043 * the data set size</em>
15045 getCount : function(){
15046 return this.data.length || 0;
15050 * Gets the total number of records in the dataset as returned by the server.
15052 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15053 * the dataset size</em>
15055 getTotalCount : function(){
15056 return this.totalLength || 0;
15060 * Returns the sort state of the Store as an object with two properties:
15062 field {String} The name of the field by which the Records are sorted
15063 direction {String} The sort order, "ASC" or "DESC"
15066 getSortState : function(){
15067 return this.sortInfo;
15071 applySort : function(){
15072 if(this.sortInfo && !this.remoteSort){
15073 var s = this.sortInfo, f = s.field;
15074 var st = this.fields.get(f).sortType;
15075 var fn = function(r1, r2){
15076 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15077 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15079 this.data.sort(s.direction, fn);
15080 if(this.snapshot && this.snapshot != this.data){
15081 this.snapshot.sort(s.direction, fn);
15087 * Sets the default sort column and order to be used by the next load operation.
15088 * @param {String} fieldName The name of the field to sort by.
15089 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15091 setDefaultSort : function(field, dir){
15092 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15096 * Sort the Records.
15097 * If remote sorting is used, the sort is performed on the server, and the cache is
15098 * reloaded. If local sorting is used, the cache is sorted internally.
15099 * @param {String} fieldName The name of the field to sort by.
15100 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15102 sort : function(fieldName, dir){
15103 var f = this.fields.get(fieldName);
15105 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15107 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15108 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15113 this.sortToggle[f.name] = dir;
15114 this.sortInfo = {field: f.name, direction: dir};
15115 if(!this.remoteSort){
15117 this.fireEvent("datachanged", this);
15119 this.load(this.lastOptions);
15124 * Calls the specified function for each of the Records in the cache.
15125 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15126 * Returning <em>false</em> aborts and exits the iteration.
15127 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15129 each : function(fn, scope){
15130 this.data.each(fn, scope);
15134 * Gets all records modified since the last commit. Modified records are persisted across load operations
15135 * (e.g., during paging).
15136 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15138 getModifiedRecords : function(){
15139 return this.modified;
15143 createFilterFn : function(property, value, anyMatch){
15144 if(!value.exec){ // not a regex
15145 value = String(value);
15146 if(value.length == 0){
15149 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15151 return function(r){
15152 return value.test(r.data[property]);
15157 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15158 * @param {String} property A field on your records
15159 * @param {Number} start The record index to start at (defaults to 0)
15160 * @param {Number} end The last record index to include (defaults to length - 1)
15161 * @return {Number} The sum
15163 sum : function(property, start, end){
15164 var rs = this.data.items, v = 0;
15165 start = start || 0;
15166 end = (end || end === 0) ? end : rs.length-1;
15168 for(var i = start; i <= end; i++){
15169 v += (rs[i].data[property] || 0);
15175 * Filter the records by a specified property.
15176 * @param {String} field A field on your records
15177 * @param {String/RegExp} value Either a string that the field
15178 * should start with or a RegExp to test against the field
15179 * @param {Boolean} anyMatch True to match any part not just the beginning
15181 filter : function(property, value, anyMatch){
15182 var fn = this.createFilterFn(property, value, anyMatch);
15183 return fn ? this.filterBy(fn) : this.clearFilter();
15187 * Filter by a function. The specified function will be called with each
15188 * record in this data source. If the function returns true the record is included,
15189 * otherwise it is filtered.
15190 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15191 * @param {Object} scope (optional) The scope of the function (defaults to this)
15193 filterBy : function(fn, scope){
15194 this.snapshot = this.snapshot || this.data;
15195 this.data = this.queryBy(fn, scope||this);
15196 this.fireEvent("datachanged", this);
15200 * Query the records by a specified property.
15201 * @param {String} field A field on your records
15202 * @param {String/RegExp} value Either a string that the field
15203 * should start with or a RegExp to test against the field
15204 * @param {Boolean} anyMatch True to match any part not just the beginning
15205 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15207 query : function(property, value, anyMatch){
15208 var fn = this.createFilterFn(property, value, anyMatch);
15209 return fn ? this.queryBy(fn) : this.data.clone();
15213 * Query by a function. The specified function will be called with each
15214 * record in this data source. If the function returns true the record is included
15216 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15217 * @param {Object} scope (optional) The scope of the function (defaults to this)
15218 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15220 queryBy : function(fn, scope){
15221 var data = this.snapshot || this.data;
15222 return data.filterBy(fn, scope||this);
15226 * Collects unique values for a particular dataIndex from this store.
15227 * @param {String} dataIndex The property to collect
15228 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15229 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15230 * @return {Array} An array of the unique values
15232 collect : function(dataIndex, allowNull, bypassFilter){
15233 var d = (bypassFilter === true && this.snapshot) ?
15234 this.snapshot.items : this.data.items;
15235 var v, sv, r = [], l = {};
15236 for(var i = 0, len = d.length; i < len; i++){
15237 v = d[i].data[dataIndex];
15239 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15248 * Revert to a view of the Record cache with no filtering applied.
15249 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15251 clearFilter : function(suppressEvent){
15252 if(this.snapshot && this.snapshot != this.data){
15253 this.data = this.snapshot;
15254 delete this.snapshot;
15255 if(suppressEvent !== true){
15256 this.fireEvent("datachanged", this);
15262 afterEdit : function(record){
15263 if(this.modified.indexOf(record) == -1){
15264 this.modified.push(record);
15266 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15270 afterReject : function(record){
15271 this.modified.remove(record);
15272 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15276 afterCommit : function(record){
15277 this.modified.remove(record);
15278 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15282 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15283 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15285 commitChanges : function(){
15286 var m = this.modified.slice(0);
15287 this.modified = [];
15288 for(var i = 0, len = m.length; i < len; i++){
15294 * Cancel outstanding changes on all changed records.
15296 rejectChanges : function(){
15297 var m = this.modified.slice(0);
15298 this.modified = [];
15299 for(var i = 0, len = m.length; i < len; i++){
15304 onMetaChange : function(meta, rtype, o){
15305 this.recordType = rtype;
15306 this.fields = rtype.prototype.fields;
15307 delete this.snapshot;
15308 this.sortInfo = meta.sortInfo || this.sortInfo;
15309 this.modified = [];
15310 this.fireEvent('metachange', this, this.reader.meta);
15313 moveIndex : function(data, type)
15315 var index = this.indexOf(data);
15317 var newIndex = index + type;
15321 this.insert(newIndex, data);
15326 * Ext JS Library 1.1.1
15327 * Copyright(c) 2006-2007, Ext JS, LLC.
15329 * Originally Released Under LGPL - original licence link has changed is not relivant.
15332 * <script type="text/javascript">
15336 * @class Roo.data.SimpleStore
15337 * @extends Roo.data.Store
15338 * Small helper class to make creating Stores from Array data easier.
15339 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15340 * @cfg {Array} fields An array of field definition objects, or field name strings.
15341 * @cfg {Object} an existing reader (eg. copied from another store)
15342 * @cfg {Array} data The multi-dimensional array of data
15344 * @param {Object} config
15346 Roo.data.SimpleStore = function(config)
15348 Roo.data.SimpleStore.superclass.constructor.call(this, {
15350 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15353 Roo.data.Record.create(config.fields)
15355 proxy : new Roo.data.MemoryProxy(config.data)
15359 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15361 * Ext JS Library 1.1.1
15362 * Copyright(c) 2006-2007, Ext JS, LLC.
15364 * Originally Released Under LGPL - original licence link has changed is not relivant.
15367 * <script type="text/javascript">
15372 * @extends Roo.data.Store
15373 * @class Roo.data.JsonStore
15374 * Small helper class to make creating Stores for JSON data easier. <br/>
15376 var store = new Roo.data.JsonStore({
15377 url: 'get-images.php',
15379 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15382 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15383 * JsonReader and HttpProxy (unless inline data is provided).</b>
15384 * @cfg {Array} fields An array of field definition objects, or field name strings.
15386 * @param {Object} config
15388 Roo.data.JsonStore = function(c){
15389 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15390 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15391 reader: new Roo.data.JsonReader(c, c.fields)
15394 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15396 * Ext JS Library 1.1.1
15397 * Copyright(c) 2006-2007, Ext JS, LLC.
15399 * Originally Released Under LGPL - original licence link has changed is not relivant.
15402 * <script type="text/javascript">
15406 Roo.data.Field = function(config){
15407 if(typeof config == "string"){
15408 config = {name: config};
15410 Roo.apply(this, config);
15413 this.type = "auto";
15416 var st = Roo.data.SortTypes;
15417 // named sortTypes are supported, here we look them up
15418 if(typeof this.sortType == "string"){
15419 this.sortType = st[this.sortType];
15422 // set default sortType for strings and dates
15423 if(!this.sortType){
15426 this.sortType = st.asUCString;
15429 this.sortType = st.asDate;
15432 this.sortType = st.none;
15437 var stripRe = /[\$,%]/g;
15439 // prebuilt conversion function for this field, instead of
15440 // switching every time we're reading a value
15442 var cv, dateFormat = this.dateFormat;
15447 cv = function(v){ return v; };
15450 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15454 return v !== undefined && v !== null && v !== '' ?
15455 parseInt(String(v).replace(stripRe, ""), 10) : '';
15460 return v !== undefined && v !== null && v !== '' ?
15461 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15466 cv = function(v){ return v === true || v === "true" || v == 1; };
15473 if(v instanceof Date){
15477 if(dateFormat == "timestamp"){
15478 return new Date(v*1000);
15480 return Date.parseDate(v, dateFormat);
15482 var parsed = Date.parse(v);
15483 return parsed ? new Date(parsed) : null;
15492 Roo.data.Field.prototype = {
15500 * Ext JS Library 1.1.1
15501 * Copyright(c) 2006-2007, Ext JS, LLC.
15503 * Originally Released Under LGPL - original licence link has changed is not relivant.
15506 * <script type="text/javascript">
15509 // Base class for reading structured data from a data source. This class is intended to be
15510 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15513 * @class Roo.data.DataReader
15514 * Base class for reading structured data from a data source. This class is intended to be
15515 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15518 Roo.data.DataReader = function(meta, recordType){
15522 this.recordType = recordType instanceof Array ?
15523 Roo.data.Record.create(recordType) : recordType;
15526 Roo.data.DataReader.prototype = {
15529 readerType : 'Data',
15531 * Create an empty record
15532 * @param {Object} data (optional) - overlay some values
15533 * @return {Roo.data.Record} record created.
15535 newRow : function(d) {
15537 this.recordType.prototype.fields.each(function(c) {
15539 case 'int' : da[c.name] = 0; break;
15540 case 'date' : da[c.name] = new Date(); break;
15541 case 'float' : da[c.name] = 0.0; break;
15542 case 'boolean' : da[c.name] = false; break;
15543 default : da[c.name] = ""; break;
15547 return new this.recordType(Roo.apply(da, d));
15553 * Ext JS Library 1.1.1
15554 * Copyright(c) 2006-2007, Ext JS, LLC.
15556 * Originally Released Under LGPL - original licence link has changed is not relivant.
15559 * <script type="text/javascript">
15563 * @class Roo.data.DataProxy
15564 * @extends Roo.data.Observable
15565 * This class is an abstract base class for implementations which provide retrieval of
15566 * unformatted data objects.<br>
15568 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15569 * (of the appropriate type which knows how to parse the data object) to provide a block of
15570 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15572 * Custom implementations must implement the load method as described in
15573 * {@link Roo.data.HttpProxy#load}.
15575 Roo.data.DataProxy = function(){
15578 * @event beforeload
15579 * Fires before a network request is made to retrieve a data object.
15580 * @param {Object} This DataProxy object.
15581 * @param {Object} params The params parameter to the load function.
15586 * Fires before the load method's callback is called.
15587 * @param {Object} This DataProxy object.
15588 * @param {Object} o The data object.
15589 * @param {Object} arg The callback argument object passed to the load function.
15593 * @event loadexception
15594 * Fires if an Exception occurs during data retrieval.
15595 * @param {Object} This DataProxy object.
15596 * @param {Object} o The data object.
15597 * @param {Object} arg The callback argument object passed to the load function.
15598 * @param {Object} e The Exception.
15600 loadexception : true
15602 Roo.data.DataProxy.superclass.constructor.call(this);
15605 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15608 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15612 * Ext JS Library 1.1.1
15613 * Copyright(c) 2006-2007, Ext JS, LLC.
15615 * Originally Released Under LGPL - original licence link has changed is not relivant.
15618 * <script type="text/javascript">
15621 * @class Roo.data.MemoryProxy
15622 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15623 * to the Reader when its load method is called.
15625 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15627 Roo.data.MemoryProxy = function(data){
15631 Roo.data.MemoryProxy.superclass.constructor.call(this);
15635 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15638 * Load data from the requested source (in this case an in-memory
15639 * data object passed to the constructor), read the data object into
15640 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15641 * process that block using the passed callback.
15642 * @param {Object} params This parameter is not used by the MemoryProxy class.
15643 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15644 * object into a block of Roo.data.Records.
15645 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15646 * The function must be passed <ul>
15647 * <li>The Record block object</li>
15648 * <li>The "arg" argument from the load function</li>
15649 * <li>A boolean success indicator</li>
15651 * @param {Object} scope The scope in which to call the callback
15652 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15654 load : function(params, reader, callback, scope, arg){
15655 params = params || {};
15658 result = reader.readRecords(params.data ? params.data :this.data);
15660 this.fireEvent("loadexception", this, arg, null, e);
15661 callback.call(scope, null, arg, false);
15664 callback.call(scope, result, arg, true);
15668 update : function(params, records){
15673 * Ext JS Library 1.1.1
15674 * Copyright(c) 2006-2007, Ext JS, LLC.
15676 * Originally Released Under LGPL - original licence link has changed is not relivant.
15679 * <script type="text/javascript">
15682 * @class Roo.data.HttpProxy
15683 * @extends Roo.data.DataProxy
15684 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15685 * configured to reference a certain URL.<br><br>
15687 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15688 * from which the running page was served.<br><br>
15690 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15692 * Be aware that to enable the browser to parse an XML document, the server must set
15693 * the Content-Type header in the HTTP response to "text/xml".
15695 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15696 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15697 * will be used to make the request.
15699 Roo.data.HttpProxy = function(conn){
15700 Roo.data.HttpProxy.superclass.constructor.call(this);
15701 // is conn a conn config or a real conn?
15703 this.useAjax = !conn || !conn.events;
15707 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15708 // thse are take from connection...
15711 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15714 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15715 * extra parameters to each request made by this object. (defaults to undefined)
15718 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15719 * to each request made by this object. (defaults to undefined)
15722 * @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)
15725 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15728 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15734 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15738 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15739 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15740 * a finer-grained basis than the DataProxy events.
15742 getConnection : function(){
15743 return this.useAjax ? Roo.Ajax : this.conn;
15747 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15748 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15749 * process that block using the passed callback.
15750 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15751 * for the request to the remote server.
15752 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15753 * object into a block of Roo.data.Records.
15754 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15755 * The function must be passed <ul>
15756 * <li>The Record block object</li>
15757 * <li>The "arg" argument from the load function</li>
15758 * <li>A boolean success indicator</li>
15760 * @param {Object} scope The scope in which to call the callback
15761 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15763 load : function(params, reader, callback, scope, arg){
15764 if(this.fireEvent("beforeload", this, params) !== false){
15766 params : params || {},
15768 callback : callback,
15773 callback : this.loadResponse,
15777 Roo.applyIf(o, this.conn);
15778 if(this.activeRequest){
15779 Roo.Ajax.abort(this.activeRequest);
15781 this.activeRequest = Roo.Ajax.request(o);
15783 this.conn.request(o);
15786 callback.call(scope||this, null, arg, false);
15791 loadResponse : function(o, success, response){
15792 delete this.activeRequest;
15794 this.fireEvent("loadexception", this, o, response);
15795 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15800 result = o.reader.read(response);
15802 this.fireEvent("loadexception", this, o, response, e);
15803 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15807 this.fireEvent("load", this, o, o.request.arg);
15808 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15812 update : function(dataSet){
15817 updateResponse : function(dataSet){
15822 * Ext JS Library 1.1.1
15823 * Copyright(c) 2006-2007, Ext JS, LLC.
15825 * Originally Released Under LGPL - original licence link has changed is not relivant.
15828 * <script type="text/javascript">
15832 * @class Roo.data.ScriptTagProxy
15833 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15834 * other than the originating domain of the running page.<br><br>
15836 * <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
15837 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15839 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15840 * source code that is used as the source inside a <script> tag.<br><br>
15842 * In order for the browser to process the returned data, the server must wrap the data object
15843 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15844 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15845 * depending on whether the callback name was passed:
15848 boolean scriptTag = false;
15849 String cb = request.getParameter("callback");
15852 response.setContentType("text/javascript");
15854 response.setContentType("application/x-json");
15856 Writer out = response.getWriter();
15858 out.write(cb + "(");
15860 out.print(dataBlock.toJsonString());
15867 * @param {Object} config A configuration object.
15869 Roo.data.ScriptTagProxy = function(config){
15870 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15871 Roo.apply(this, config);
15872 this.head = document.getElementsByTagName("head")[0];
15875 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15877 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15879 * @cfg {String} url The URL from which to request the data object.
15882 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15886 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15887 * the server the name of the callback function set up by the load call to process the returned data object.
15888 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15889 * javascript output which calls this named function passing the data object as its only parameter.
15891 callbackParam : "callback",
15893 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15894 * name to the request.
15899 * Load data from the configured URL, read the data object into
15900 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15901 * process that block using the passed callback.
15902 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15903 * for the request to the remote server.
15904 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15905 * object into a block of Roo.data.Records.
15906 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15907 * The function must be passed <ul>
15908 * <li>The Record block object</li>
15909 * <li>The "arg" argument from the load function</li>
15910 * <li>A boolean success indicator</li>
15912 * @param {Object} scope The scope in which to call the callback
15913 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15915 load : function(params, reader, callback, scope, arg){
15916 if(this.fireEvent("beforeload", this, params) !== false){
15918 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15920 var url = this.url;
15921 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15923 url += "&_dc=" + (new Date().getTime());
15925 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15928 cb : "stcCallback"+transId,
15929 scriptId : "stcScript"+transId,
15933 callback : callback,
15939 window[trans.cb] = function(o){
15940 conn.handleResponse(o, trans);
15943 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15945 if(this.autoAbort !== false){
15949 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15951 var script = document.createElement("script");
15952 script.setAttribute("src", url);
15953 script.setAttribute("type", "text/javascript");
15954 script.setAttribute("id", trans.scriptId);
15955 this.head.appendChild(script);
15957 this.trans = trans;
15959 callback.call(scope||this, null, arg, false);
15964 isLoading : function(){
15965 return this.trans ? true : false;
15969 * Abort the current server request.
15971 abort : function(){
15972 if(this.isLoading()){
15973 this.destroyTrans(this.trans);
15978 destroyTrans : function(trans, isLoaded){
15979 this.head.removeChild(document.getElementById(trans.scriptId));
15980 clearTimeout(trans.timeoutId);
15982 window[trans.cb] = undefined;
15984 delete window[trans.cb];
15987 // if hasn't been loaded, wait for load to remove it to prevent script error
15988 window[trans.cb] = function(){
15989 window[trans.cb] = undefined;
15991 delete window[trans.cb];
15998 handleResponse : function(o, trans){
15999 this.trans = false;
16000 this.destroyTrans(trans, true);
16003 result = trans.reader.readRecords(o);
16005 this.fireEvent("loadexception", this, o, trans.arg, e);
16006 trans.callback.call(trans.scope||window, null, trans.arg, false);
16009 this.fireEvent("load", this, o, trans.arg);
16010 trans.callback.call(trans.scope||window, result, trans.arg, true);
16014 handleFailure : function(trans){
16015 this.trans = false;
16016 this.destroyTrans(trans, false);
16017 this.fireEvent("loadexception", this, null, trans.arg);
16018 trans.callback.call(trans.scope||window, null, trans.arg, false);
16022 * Ext JS Library 1.1.1
16023 * Copyright(c) 2006-2007, Ext JS, LLC.
16025 * Originally Released Under LGPL - original licence link has changed is not relivant.
16028 * <script type="text/javascript">
16032 * @class Roo.data.JsonReader
16033 * @extends Roo.data.DataReader
16034 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16035 * based on mappings in a provided Roo.data.Record constructor.
16037 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16038 * in the reply previously.
16043 var RecordDef = Roo.data.Record.create([
16044 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16045 {name: 'occupation'} // This field will use "occupation" as the mapping.
16047 var myReader = new Roo.data.JsonReader({
16048 totalProperty: "results", // The property which contains the total dataset size (optional)
16049 root: "rows", // The property which contains an Array of row objects
16050 id: "id" // The property within each row object that provides an ID for the record (optional)
16054 * This would consume a JSON file like this:
16056 { 'results': 2, 'rows': [
16057 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16058 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16061 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16062 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16063 * paged from the remote server.
16064 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16065 * @cfg {String} root name of the property which contains the Array of row objects.
16066 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16067 * @cfg {Array} fields Array of field definition objects
16069 * Create a new JsonReader
16070 * @param {Object} meta Metadata configuration options
16071 * @param {Object} recordType Either an Array of field definition objects,
16072 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16074 Roo.data.JsonReader = function(meta, recordType){
16077 // set some defaults:
16078 Roo.applyIf(meta, {
16079 totalProperty: 'total',
16080 successProperty : 'success',
16085 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16087 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16089 readerType : 'Json',
16092 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16093 * Used by Store query builder to append _requestMeta to params.
16096 metaFromRemote : false,
16098 * This method is only used by a DataProxy which has retrieved data from a remote server.
16099 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16100 * @return {Object} data A data block which is used by an Roo.data.Store object as
16101 * a cache of Roo.data.Records.
16103 read : function(response){
16104 var json = response.responseText;
16106 var o = /* eval:var:o */ eval("("+json+")");
16108 throw {message: "JsonReader.read: Json object not found"};
16114 this.metaFromRemote = true;
16115 this.meta = o.metaData;
16116 this.recordType = Roo.data.Record.create(o.metaData.fields);
16117 this.onMetaChange(this.meta, this.recordType, o);
16119 return this.readRecords(o);
16122 // private function a store will implement
16123 onMetaChange : function(meta, recordType, o){
16130 simpleAccess: function(obj, subsc) {
16137 getJsonAccessor: function(){
16139 return function(expr) {
16141 return(re.test(expr))
16142 ? new Function("obj", "return obj." + expr)
16147 return Roo.emptyFn;
16152 * Create a data block containing Roo.data.Records from an XML document.
16153 * @param {Object} o An object which contains an Array of row objects in the property specified
16154 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16155 * which contains the total size of the dataset.
16156 * @return {Object} data A data block which is used by an Roo.data.Store object as
16157 * a cache of Roo.data.Records.
16159 readRecords : function(o){
16161 * After any data loads, the raw JSON data is available for further custom processing.
16165 var s = this.meta, Record = this.recordType,
16166 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16168 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16170 if(s.totalProperty) {
16171 this.getTotal = this.getJsonAccessor(s.totalProperty);
16173 if(s.successProperty) {
16174 this.getSuccess = this.getJsonAccessor(s.successProperty);
16176 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16178 var g = this.getJsonAccessor(s.id);
16179 this.getId = function(rec) {
16181 return (r === undefined || r === "") ? null : r;
16184 this.getId = function(){return null;};
16187 for(var jj = 0; jj < fl; jj++){
16189 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16190 this.ef[jj] = this.getJsonAccessor(map);
16194 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16195 if(s.totalProperty){
16196 var vt = parseInt(this.getTotal(o), 10);
16201 if(s.successProperty){
16202 var vs = this.getSuccess(o);
16203 if(vs === false || vs === 'false'){
16208 for(var i = 0; i < c; i++){
16211 var id = this.getId(n);
16212 for(var j = 0; j < fl; j++){
16214 var v = this.ef[j](n);
16216 Roo.log('missing convert for ' + f.name);
16220 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16222 var record = new Record(values, id);
16224 records[i] = record;
16230 totalRecords : totalRecords
16233 // used when loading children.. @see loadDataFromChildren
16234 toLoadData: function(rec)
16236 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16237 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16238 return { data : data, total : data.length };
16243 * Ext JS Library 1.1.1
16244 * Copyright(c) 2006-2007, Ext JS, LLC.
16246 * Originally Released Under LGPL - original licence link has changed is not relivant.
16249 * <script type="text/javascript">
16253 * @class Roo.data.ArrayReader
16254 * @extends Roo.data.DataReader
16255 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16256 * Each element of that Array represents a row of data fields. The
16257 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16258 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16262 var RecordDef = Roo.data.Record.create([
16263 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16264 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16266 var myReader = new Roo.data.ArrayReader({
16267 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16271 * This would consume an Array like this:
16273 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16277 * Create a new JsonReader
16278 * @param {Object} meta Metadata configuration options.
16279 * @param {Object|Array} recordType Either an Array of field definition objects
16281 * @cfg {Array} fields Array of field definition objects
16282 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16283 * as specified to {@link Roo.data.Record#create},
16284 * or an {@link Roo.data.Record} object
16287 * created using {@link Roo.data.Record#create}.
16289 Roo.data.ArrayReader = function(meta, recordType)
16291 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16294 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16297 * Create a data block containing Roo.data.Records from an XML document.
16298 * @param {Object} o An Array of row objects which represents the dataset.
16299 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16300 * a cache of Roo.data.Records.
16302 readRecords : function(o)
16304 var sid = this.meta ? this.meta.id : null;
16305 var recordType = this.recordType, fields = recordType.prototype.fields;
16308 for(var i = 0; i < root.length; i++){
16311 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16312 for(var j = 0, jlen = fields.length; j < jlen; j++){
16313 var f = fields.items[j];
16314 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16315 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16317 values[f.name] = v;
16319 var record = new recordType(values, id);
16321 records[records.length] = record;
16325 totalRecords : records.length
16328 // used when loading children.. @see loadDataFromChildren
16329 toLoadData: function(rec)
16331 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16332 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16343 * @class Roo.bootstrap.ComboBox
16344 * @extends Roo.bootstrap.TriggerField
16345 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16346 * @cfg {Boolean} append (true|false) default false
16347 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16348 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16349 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16350 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16351 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16352 * @cfg {Boolean} animate default true
16353 * @cfg {Boolean} emptyResultText only for touch device
16354 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16355 * @cfg {String} emptyTitle default ''
16356 * @cfg {Number} width fixed with? experimental
16358 * Create a new ComboBox.
16359 * @param {Object} config Configuration options
16361 Roo.bootstrap.ComboBox = function(config){
16362 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16366 * Fires when the dropdown list is expanded
16367 * @param {Roo.bootstrap.ComboBox} combo This combo box
16372 * Fires when the dropdown list is collapsed
16373 * @param {Roo.bootstrap.ComboBox} combo This combo box
16377 * @event beforeselect
16378 * Fires before a list item is selected. Return false to cancel the selection.
16379 * @param {Roo.bootstrap.ComboBox} combo This combo box
16380 * @param {Roo.data.Record} record The data record returned from the underlying store
16381 * @param {Number} index The index of the selected item in the dropdown list
16383 'beforeselect' : true,
16386 * Fires when a list item is selected
16387 * @param {Roo.bootstrap.ComboBox} combo This combo box
16388 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16389 * @param {Number} index The index of the selected item in the dropdown list
16393 * @event beforequery
16394 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16395 * The event object passed has these properties:
16396 * @param {Roo.bootstrap.ComboBox} combo This combo box
16397 * @param {String} query The query
16398 * @param {Boolean} forceAll true to force "all" query
16399 * @param {Boolean} cancel true to cancel the query
16400 * @param {Object} e The query event object
16402 'beforequery': true,
16405 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16406 * @param {Roo.bootstrap.ComboBox} combo This combo box
16411 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16412 * @param {Roo.bootstrap.ComboBox} combo This combo box
16413 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16418 * Fires when the remove value from the combobox array
16419 * @param {Roo.bootstrap.ComboBox} combo This combo box
16423 * @event afterremove
16424 * Fires when the remove value from the combobox array
16425 * @param {Roo.bootstrap.ComboBox} combo This combo box
16427 'afterremove' : true,
16429 * @event specialfilter
16430 * Fires when specialfilter
16431 * @param {Roo.bootstrap.ComboBox} combo This combo box
16433 'specialfilter' : true,
16436 * Fires when tick the element
16437 * @param {Roo.bootstrap.ComboBox} combo This combo box
16441 * @event touchviewdisplay
16442 * Fires when touch view require special display (default is using displayField)
16443 * @param {Roo.bootstrap.ComboBox} combo This combo box
16444 * @param {Object} cfg set html .
16446 'touchviewdisplay' : true
16451 this.tickItems = [];
16453 this.selectedIndex = -1;
16454 if(this.mode == 'local'){
16455 if(config.queryDelay === undefined){
16456 this.queryDelay = 10;
16458 if(config.minChars === undefined){
16464 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16467 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16468 * rendering into an Roo.Editor, defaults to false)
16471 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16472 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16475 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16478 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16479 * the dropdown list (defaults to undefined, with no header element)
16483 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16487 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16489 listWidth: undefined,
16491 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16492 * mode = 'remote' or 'text' if mode = 'local')
16494 displayField: undefined,
16497 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16498 * mode = 'remote' or 'value' if mode = 'local').
16499 * Note: use of a valueField requires the user make a selection
16500 * in order for a value to be mapped.
16502 valueField: undefined,
16504 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16509 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16510 * field's data value (defaults to the underlying DOM element's name)
16512 hiddenName: undefined,
16514 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16518 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16520 selectedClass: 'active',
16523 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16527 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16528 * anchor positions (defaults to 'tl-bl')
16530 listAlign: 'tl-bl?',
16532 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16536 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16537 * query specified by the allQuery config option (defaults to 'query')
16539 triggerAction: 'query',
16541 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16542 * (defaults to 4, does not apply if editable = false)
16546 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16547 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16551 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16552 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16556 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16557 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16561 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16562 * when editable = true (defaults to false)
16564 selectOnFocus:false,
16566 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16568 queryParam: 'query',
16570 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16571 * when mode = 'remote' (defaults to 'Loading...')
16573 loadingText: 'Loading...',
16575 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16579 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16583 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16584 * traditional select (defaults to true)
16588 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16592 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16596 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16597 * listWidth has a higher value)
16601 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16602 * allow the user to set arbitrary text into the field (defaults to false)
16604 forceSelection:false,
16606 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16607 * if typeAhead = true (defaults to 250)
16609 typeAheadDelay : 250,
16611 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16612 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16614 valueNotFoundText : undefined,
16616 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16618 blockFocus : false,
16621 * @cfg {Boolean} disableClear Disable showing of clear button.
16623 disableClear : false,
16625 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16627 alwaysQuery : false,
16630 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16635 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16637 invalidClass : "has-warning",
16640 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16642 validClass : "has-success",
16645 * @cfg {Boolean} specialFilter (true|false) special filter default false
16647 specialFilter : false,
16650 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16652 mobileTouchView : true,
16655 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16657 useNativeIOS : false,
16660 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16662 mobile_restrict_height : false,
16664 ios_options : false,
16676 btnPosition : 'right',
16677 triggerList : true,
16678 showToggleBtn : true,
16680 emptyResultText: 'Empty',
16681 triggerText : 'Select',
16685 // element that contains real text value.. (when hidden is used..)
16687 getAutoCreate : function()
16692 * Render classic select for iso
16695 if(Roo.isIOS && this.useNativeIOS){
16696 cfg = this.getAutoCreateNativeIOS();
16704 if(Roo.isTouch && this.mobileTouchView){
16705 cfg = this.getAutoCreateTouchView();
16712 if(!this.tickable){
16713 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16718 * ComboBox with tickable selections
16721 var align = this.labelAlign || this.parentLabelAlign();
16724 cls : 'form-group roo-combobox-tickable' //input-group
16727 var btn_text_select = '';
16728 var btn_text_done = '';
16729 var btn_text_cancel = '';
16731 if (this.btn_text_show) {
16732 btn_text_select = 'Select';
16733 btn_text_done = 'Done';
16734 btn_text_cancel = 'Cancel';
16739 cls : 'tickable-buttons',
16744 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16745 //html : this.triggerText
16746 html: btn_text_select
16752 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16754 html: btn_text_done
16760 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16762 html: btn_text_cancel
16768 buttons.cn.unshift({
16770 cls: 'roo-select2-search-field-input'
16776 Roo.each(buttons.cn, function(c){
16778 c.cls += ' btn-' + _this.size;
16781 if (_this.disabled) {
16788 style : 'display: contents',
16793 cls: 'form-hidden-field'
16797 cls: 'roo-select2-choices',
16801 cls: 'roo-select2-search-field',
16812 cls: 'roo-select2-container input-group roo-select2-container-multi',
16818 // cls: 'typeahead typeahead-long dropdown-menu',
16819 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16824 if(this.hasFeedback && !this.allowBlank){
16828 cls: 'glyphicon form-control-feedback'
16831 combobox.cn.push(feedback);
16838 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16839 tooltip : 'This field is required'
16841 if (Roo.bootstrap.version == 4) {
16844 style : 'display:none'
16847 if (align ==='left' && this.fieldLabel.length) {
16849 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16856 cls : 'control-label col-form-label',
16857 html : this.fieldLabel
16869 var labelCfg = cfg.cn[1];
16870 var contentCfg = cfg.cn[2];
16873 if(this.indicatorpos == 'right'){
16879 cls : 'control-label col-form-label',
16883 html : this.fieldLabel
16899 labelCfg = cfg.cn[0];
16900 contentCfg = cfg.cn[1];
16904 if(this.labelWidth > 12){
16905 labelCfg.style = "width: " + this.labelWidth + 'px';
16907 if(this.width * 1 > 0){
16908 contentCfg.style = "width: " + this.width + 'px';
16910 if(this.labelWidth < 13 && this.labelmd == 0){
16911 this.labelmd = this.labelWidth;
16914 if(this.labellg > 0){
16915 labelCfg.cls += ' col-lg-' + this.labellg;
16916 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16919 if(this.labelmd > 0){
16920 labelCfg.cls += ' col-md-' + this.labelmd;
16921 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16924 if(this.labelsm > 0){
16925 labelCfg.cls += ' col-sm-' + this.labelsm;
16926 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16929 if(this.labelxs > 0){
16930 labelCfg.cls += ' col-xs-' + this.labelxs;
16931 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16935 } else if ( this.fieldLabel.length) {
16936 // Roo.log(" label");
16941 //cls : 'input-group-addon',
16942 html : this.fieldLabel
16947 if(this.indicatorpos == 'right'){
16951 //cls : 'input-group-addon',
16952 html : this.fieldLabel
16962 // Roo.log(" no label && no align");
16969 ['xs','sm','md','lg'].map(function(size){
16970 if (settings[size]) {
16971 cfg.cls += ' col-' + size + '-' + settings[size];
16979 _initEventsCalled : false,
16982 initEvents: function()
16984 if (this._initEventsCalled) { // as we call render... prevent looping...
16987 this._initEventsCalled = true;
16990 throw "can not find store for combo";
16993 this.indicator = this.indicatorEl();
16995 this.store = Roo.factory(this.store, Roo.data);
16996 this.store.parent = this;
16998 // if we are building from html. then this element is so complex, that we can not really
16999 // use the rendered HTML.
17000 // so we have to trash and replace the previous code.
17001 if (Roo.XComponent.build_from_html) {
17002 // remove this element....
17003 var e = this.el.dom, k=0;
17004 while (e ) { e = e.previousSibling; ++k;}
17009 this.rendered = false;
17011 this.render(this.parent().getChildContainer(true), k);
17014 if(Roo.isIOS && this.useNativeIOS){
17015 this.initIOSView();
17023 if(Roo.isTouch && this.mobileTouchView){
17024 this.initTouchView();
17029 this.initTickableEvents();
17033 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17035 if(this.hiddenName){
17037 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17039 this.hiddenField.dom.value =
17040 this.hiddenValue !== undefined ? this.hiddenValue :
17041 this.value !== undefined ? this.value : '';
17043 // prevent input submission
17044 this.el.dom.removeAttribute('name');
17045 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17050 // this.el.dom.setAttribute('autocomplete', 'off');
17053 var cls = 'x-combo-list';
17055 //this.list = new Roo.Layer({
17056 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17062 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17063 _this.list.setWidth(lw);
17066 this.list.on('mouseover', this.onViewOver, this);
17067 this.list.on('mousemove', this.onViewMove, this);
17068 this.list.on('scroll', this.onViewScroll, this);
17071 this.list.swallowEvent('mousewheel');
17072 this.assetHeight = 0;
17075 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17076 this.assetHeight += this.header.getHeight();
17079 this.innerList = this.list.createChild({cls:cls+'-inner'});
17080 this.innerList.on('mouseover', this.onViewOver, this);
17081 this.innerList.on('mousemove', this.onViewMove, this);
17082 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17084 if(this.allowBlank && !this.pageSize && !this.disableClear){
17085 this.footer = this.list.createChild({cls:cls+'-ft'});
17086 this.pageTb = new Roo.Toolbar(this.footer);
17090 this.footer = this.list.createChild({cls:cls+'-ft'});
17091 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17092 {pageSize: this.pageSize});
17096 if (this.pageTb && this.allowBlank && !this.disableClear) {
17098 this.pageTb.add(new Roo.Toolbar.Fill(), {
17099 cls: 'x-btn-icon x-btn-clear',
17101 handler: function()
17104 _this.clearValue();
17105 _this.onSelect(false, -1);
17110 this.assetHeight += this.footer.getHeight();
17115 this.tpl = Roo.bootstrap.version == 4 ?
17116 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17117 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17120 this.view = new Roo.View(this.list, this.tpl, {
17121 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17123 //this.view.wrapEl.setDisplayed(false);
17124 this.view.on('click', this.onViewClick, this);
17127 this.store.on('beforeload', this.onBeforeLoad, this);
17128 this.store.on('load', this.onLoad, this);
17129 this.store.on('loadexception', this.onLoadException, this);
17131 if(this.resizable){
17132 this.resizer = new Roo.Resizable(this.list, {
17133 pinned:true, handles:'se'
17135 this.resizer.on('resize', function(r, w, h){
17136 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17137 this.listWidth = w;
17138 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17139 this.restrictHeight();
17141 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17144 if(!this.editable){
17145 this.editable = true;
17146 this.setEditable(false);
17151 if (typeof(this.events.add.listeners) != 'undefined') {
17153 this.addicon = this.wrap.createChild(
17154 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17156 this.addicon.on('click', function(e) {
17157 this.fireEvent('add', this);
17160 if (typeof(this.events.edit.listeners) != 'undefined') {
17162 this.editicon = this.wrap.createChild(
17163 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17164 if (this.addicon) {
17165 this.editicon.setStyle('margin-left', '40px');
17167 this.editicon.on('click', function(e) {
17169 // we fire even if inothing is selected..
17170 this.fireEvent('edit', this, this.lastData );
17176 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17177 "up" : function(e){
17178 this.inKeyMode = true;
17182 "down" : function(e){
17183 if(!this.isExpanded()){
17184 this.onTriggerClick();
17186 this.inKeyMode = true;
17191 "enter" : function(e){
17192 // this.onViewClick();
17196 if(this.fireEvent("specialkey", this, e)){
17197 this.onViewClick(false);
17203 "esc" : function(e){
17207 "tab" : function(e){
17210 if(this.fireEvent("specialkey", this, e)){
17211 this.onViewClick(false);
17219 doRelay : function(foo, bar, hname){
17220 if(hname == 'down' || this.scope.isExpanded()){
17221 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17230 this.queryDelay = Math.max(this.queryDelay || 10,
17231 this.mode == 'local' ? 10 : 250);
17234 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17236 if(this.typeAhead){
17237 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17239 if(this.editable !== false){
17240 this.inputEl().on("keyup", this.onKeyUp, this);
17242 if(this.forceSelection){
17243 this.inputEl().on('blur', this.doForce, this);
17247 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17248 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17252 initTickableEvents: function()
17256 if(this.hiddenName){
17258 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17260 this.hiddenField.dom.value =
17261 this.hiddenValue !== undefined ? this.hiddenValue :
17262 this.value !== undefined ? this.value : '';
17264 // prevent input submission
17265 this.el.dom.removeAttribute('name');
17266 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17271 // this.list = this.el.select('ul.dropdown-menu',true).first();
17273 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17274 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17275 if(this.triggerList){
17276 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17279 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17280 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17282 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17283 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17285 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17286 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17288 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17289 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17290 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17293 this.cancelBtn.hide();
17298 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17299 _this.list.setWidth(lw);
17302 this.list.on('mouseover', this.onViewOver, this);
17303 this.list.on('mousemove', this.onViewMove, this);
17305 this.list.on('scroll', this.onViewScroll, this);
17308 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17309 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17312 this.view = new Roo.View(this.list, this.tpl, {
17317 selectedClass: this.selectedClass
17320 //this.view.wrapEl.setDisplayed(false);
17321 this.view.on('click', this.onViewClick, this);
17325 this.store.on('beforeload', this.onBeforeLoad, this);
17326 this.store.on('load', this.onLoad, this);
17327 this.store.on('loadexception', this.onLoadException, this);
17330 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17331 "up" : function(e){
17332 this.inKeyMode = true;
17336 "down" : function(e){
17337 this.inKeyMode = true;
17341 "enter" : function(e){
17342 if(this.fireEvent("specialkey", this, e)){
17343 this.onViewClick(false);
17349 "esc" : function(e){
17350 this.onTickableFooterButtonClick(e, false, false);
17353 "tab" : function(e){
17354 this.fireEvent("specialkey", this, e);
17356 this.onTickableFooterButtonClick(e, false, false);
17363 doRelay : function(e, fn, key){
17364 if(this.scope.isExpanded()){
17365 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17374 this.queryDelay = Math.max(this.queryDelay || 10,
17375 this.mode == 'local' ? 10 : 250);
17378 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17380 if(this.typeAhead){
17381 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17384 if(this.editable !== false){
17385 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17388 this.indicator = this.indicatorEl();
17390 if(this.indicator){
17391 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17392 this.indicator.hide();
17397 onDestroy : function(){
17399 this.view.setStore(null);
17400 this.view.el.removeAllListeners();
17401 this.view.el.remove();
17402 this.view.purgeListeners();
17405 this.list.dom.innerHTML = '';
17409 this.store.un('beforeload', this.onBeforeLoad, this);
17410 this.store.un('load', this.onLoad, this);
17411 this.store.un('loadexception', this.onLoadException, this);
17413 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17417 fireKey : function(e){
17418 if(e.isNavKeyPress() && !this.list.isVisible()){
17419 this.fireEvent("specialkey", this, e);
17424 onResize: function(w, h)
17428 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17430 // if(typeof w != 'number'){
17431 // // we do not handle it!?!?
17434 // var tw = this.trigger.getWidth();
17435 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17436 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17438 // this.inputEl().setWidth( this.adjustWidth('input', x));
17440 // //this.trigger.setStyle('left', x+'px');
17442 // if(this.list && this.listWidth === undefined){
17443 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17444 // this.list.setWidth(lw);
17445 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17453 * Allow or prevent the user from directly editing the field text. If false is passed,
17454 * the user will only be able to select from the items defined in the dropdown list. This method
17455 * is the runtime equivalent of setting the 'editable' config option at config time.
17456 * @param {Boolean} value True to allow the user to directly edit the field text
17458 setEditable : function(value){
17459 if(value == this.editable){
17462 this.editable = value;
17464 this.inputEl().dom.setAttribute('readOnly', true);
17465 this.inputEl().on('mousedown', this.onTriggerClick, this);
17466 this.inputEl().addClass('x-combo-noedit');
17468 this.inputEl().dom.removeAttribute('readOnly');
17469 this.inputEl().un('mousedown', this.onTriggerClick, this);
17470 this.inputEl().removeClass('x-combo-noedit');
17476 onBeforeLoad : function(combo,opts){
17477 if(!this.hasFocus){
17481 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17483 this.restrictHeight();
17484 this.selectedIndex = -1;
17488 onLoad : function(){
17490 this.hasQuery = false;
17492 if(!this.hasFocus){
17496 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17497 this.loading.hide();
17500 if(this.store.getCount() > 0){
17503 this.restrictHeight();
17504 if(this.lastQuery == this.allQuery){
17505 if(this.editable && !this.tickable){
17506 this.inputEl().dom.select();
17510 !this.selectByValue(this.value, true) &&
17513 !this.store.lastOptions ||
17514 typeof(this.store.lastOptions.add) == 'undefined' ||
17515 this.store.lastOptions.add != true
17518 this.select(0, true);
17521 if(this.autoFocus){
17524 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17525 this.taTask.delay(this.typeAheadDelay);
17529 this.onEmptyResults();
17535 onLoadException : function()
17537 this.hasQuery = false;
17539 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17540 this.loading.hide();
17543 if(this.tickable && this.editable){
17548 // only causes errors at present
17549 //Roo.log(this.store.reader.jsonData);
17550 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17552 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17558 onTypeAhead : function(){
17559 if(this.store.getCount() > 0){
17560 var r = this.store.getAt(0);
17561 var newValue = r.data[this.displayField];
17562 var len = newValue.length;
17563 var selStart = this.getRawValue().length;
17565 if(selStart != len){
17566 this.setRawValue(newValue);
17567 this.selectText(selStart, newValue.length);
17573 onSelect : function(record, index){
17575 if(this.fireEvent('beforeselect', this, record, index) !== false){
17577 this.setFromData(index > -1 ? record.data : false);
17580 this.fireEvent('select', this, record, index);
17585 * Returns the currently selected field value or empty string if no value is set.
17586 * @return {String} value The selected value
17588 getValue : function()
17590 if(Roo.isIOS && this.useNativeIOS){
17591 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17595 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17598 if(this.valueField){
17599 return typeof this.value != 'undefined' ? this.value : '';
17601 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17605 getRawValue : function()
17607 if(Roo.isIOS && this.useNativeIOS){
17608 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17611 var v = this.inputEl().getValue();
17617 * Clears any text/value currently set in the field
17619 clearValue : function(){
17621 if(this.hiddenField){
17622 this.hiddenField.dom.value = '';
17625 this.setRawValue('');
17626 this.lastSelectionText = '';
17627 this.lastData = false;
17629 var close = this.closeTriggerEl();
17640 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17641 * will be displayed in the field. If the value does not match the data value of an existing item,
17642 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17643 * Otherwise the field will be blank (although the value will still be set).
17644 * @param {String} value The value to match
17646 setValue : function(v)
17648 if(Roo.isIOS && this.useNativeIOS){
17649 this.setIOSValue(v);
17659 if(this.valueField){
17660 var r = this.findRecord(this.valueField, v);
17662 text = r.data[this.displayField];
17663 }else if(this.valueNotFoundText !== undefined){
17664 text = this.valueNotFoundText;
17667 this.lastSelectionText = text;
17668 if(this.hiddenField){
17669 this.hiddenField.dom.value = v;
17671 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17674 var close = this.closeTriggerEl();
17677 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17683 * @property {Object} the last set data for the element
17688 * Sets the value of the field based on a object which is related to the record format for the store.
17689 * @param {Object} value the value to set as. or false on reset?
17691 setFromData : function(o){
17698 var dv = ''; // display value
17699 var vv = ''; // value value..
17701 if (this.displayField) {
17702 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17704 // this is an error condition!!!
17705 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17708 if(this.valueField){
17709 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17712 var close = this.closeTriggerEl();
17715 if(dv.length || vv * 1 > 0){
17717 this.blockFocus=true;
17723 if(this.hiddenField){
17724 this.hiddenField.dom.value = vv;
17726 this.lastSelectionText = dv;
17727 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17731 // no hidden field.. - we store the value in 'value', but still display
17732 // display field!!!!
17733 this.lastSelectionText = dv;
17734 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17741 reset : function(){
17742 // overridden so that last data is reset..
17749 this.setValue(this.originalValue);
17750 //this.clearInvalid();
17751 this.lastData = false;
17753 this.view.clearSelections();
17759 findRecord : function(prop, value){
17761 if(this.store.getCount() > 0){
17762 this.store.each(function(r){
17763 if(r.data[prop] == value){
17773 getName: function()
17775 // returns hidden if it's set..
17776 if (!this.rendered) {return ''};
17777 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17781 onViewMove : function(e, t){
17782 this.inKeyMode = false;
17786 onViewOver : function(e, t){
17787 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17790 var item = this.view.findItemFromChild(t);
17793 var index = this.view.indexOf(item);
17794 this.select(index, false);
17799 onViewClick : function(view, doFocus, el, e)
17801 var index = this.view.getSelectedIndexes()[0];
17803 var r = this.store.getAt(index);
17807 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17814 Roo.each(this.tickItems, function(v,k){
17816 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17818 _this.tickItems.splice(k, 1);
17820 if(typeof(e) == 'undefined' && view == false){
17821 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17833 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17834 this.tickItems.push(r.data);
17837 if(typeof(e) == 'undefined' && view == false){
17838 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17845 this.onSelect(r, index);
17847 if(doFocus !== false && !this.blockFocus){
17848 this.inputEl().focus();
17853 restrictHeight : function(){
17854 //this.innerList.dom.style.height = '';
17855 //var inner = this.innerList.dom;
17856 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17857 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17858 //this.list.beginUpdate();
17859 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17860 this.list.alignTo(this.inputEl(), this.listAlign);
17861 this.list.alignTo(this.inputEl(), this.listAlign);
17862 //this.list.endUpdate();
17866 onEmptyResults : function(){
17868 if(this.tickable && this.editable){
17869 this.hasFocus = false;
17870 this.restrictHeight();
17878 * Returns true if the dropdown list is expanded, else false.
17880 isExpanded : function(){
17881 return this.list.isVisible();
17885 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17886 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17887 * @param {String} value The data value of the item to select
17888 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17889 * selected item if it is not currently in view (defaults to true)
17890 * @return {Boolean} True if the value matched an item in the list, else false
17892 selectByValue : function(v, scrollIntoView){
17893 if(v !== undefined && v !== null){
17894 var r = this.findRecord(this.valueField || this.displayField, v);
17896 this.select(this.store.indexOf(r), scrollIntoView);
17904 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17905 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17906 * @param {Number} index The zero-based index of the list item to select
17907 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17908 * selected item if it is not currently in view (defaults to true)
17910 select : function(index, scrollIntoView){
17911 this.selectedIndex = index;
17912 this.view.select(index);
17913 if(scrollIntoView !== false){
17914 var el = this.view.getNode(index);
17916 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17919 this.list.scrollChildIntoView(el, false);
17925 selectNext : function(){
17926 var ct = this.store.getCount();
17928 if(this.selectedIndex == -1){
17930 }else if(this.selectedIndex < ct-1){
17931 this.select(this.selectedIndex+1);
17937 selectPrev : function(){
17938 var ct = this.store.getCount();
17940 if(this.selectedIndex == -1){
17942 }else if(this.selectedIndex != 0){
17943 this.select(this.selectedIndex-1);
17949 onKeyUp : function(e){
17950 if(this.editable !== false && !e.isSpecialKey()){
17951 this.lastKey = e.getKey();
17952 this.dqTask.delay(this.queryDelay);
17957 validateBlur : function(){
17958 return !this.list || !this.list.isVisible();
17962 initQuery : function(){
17964 var v = this.getRawValue();
17966 if(this.tickable && this.editable){
17967 v = this.tickableInputEl().getValue();
17974 doForce : function(){
17975 if(this.inputEl().dom.value.length > 0){
17976 this.inputEl().dom.value =
17977 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17983 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17984 * query allowing the query action to be canceled if needed.
17985 * @param {String} query The SQL query to execute
17986 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17987 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17988 * saved in the current store (defaults to false)
17990 doQuery : function(q, forceAll){
17992 if(q === undefined || q === null){
17997 forceAll: forceAll,
18001 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18006 forceAll = qe.forceAll;
18007 if(forceAll === true || (q.length >= this.minChars)){
18009 this.hasQuery = true;
18011 if(this.lastQuery != q || this.alwaysQuery){
18012 this.lastQuery = q;
18013 if(this.mode == 'local'){
18014 this.selectedIndex = -1;
18016 this.store.clearFilter();
18019 if(this.specialFilter){
18020 this.fireEvent('specialfilter', this);
18025 this.store.filter(this.displayField, q);
18028 this.store.fireEvent("datachanged", this.store);
18035 this.store.baseParams[this.queryParam] = q;
18037 var options = {params : this.getParams(q)};
18040 options.add = true;
18041 options.params.start = this.page * this.pageSize;
18044 this.store.load(options);
18047 * this code will make the page width larger, at the beginning, the list not align correctly,
18048 * we should expand the list on onLoad
18049 * so command out it
18054 this.selectedIndex = -1;
18059 this.loadNext = false;
18063 getParams : function(q){
18065 //p[this.queryParam] = q;
18069 p.limit = this.pageSize;
18075 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18077 collapse : function(){
18078 if(!this.isExpanded()){
18084 this.hasFocus = false;
18088 this.cancelBtn.hide();
18089 this.trigger.show();
18092 this.tickableInputEl().dom.value = '';
18093 this.tickableInputEl().blur();
18098 Roo.get(document).un('mousedown', this.collapseIf, this);
18099 Roo.get(document).un('mousewheel', this.collapseIf, this);
18100 if (!this.editable) {
18101 Roo.get(document).un('keydown', this.listKeyPress, this);
18103 this.fireEvent('collapse', this);
18109 collapseIf : function(e){
18110 var in_combo = e.within(this.el);
18111 var in_list = e.within(this.list);
18112 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18114 if (in_combo || in_list || is_list) {
18115 //e.stopPropagation();
18120 this.onTickableFooterButtonClick(e, false, false);
18128 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18130 expand : function(){
18132 if(this.isExpanded() || !this.hasFocus){
18136 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18137 this.list.setWidth(lw);
18143 this.restrictHeight();
18147 this.tickItems = Roo.apply([], this.item);
18150 this.cancelBtn.show();
18151 this.trigger.hide();
18154 this.tickableInputEl().focus();
18159 Roo.get(document).on('mousedown', this.collapseIf, this);
18160 Roo.get(document).on('mousewheel', this.collapseIf, this);
18161 if (!this.editable) {
18162 Roo.get(document).on('keydown', this.listKeyPress, this);
18165 this.fireEvent('expand', this);
18169 // Implements the default empty TriggerField.onTriggerClick function
18170 onTriggerClick : function(e)
18172 Roo.log('trigger click');
18174 if(this.disabled || !this.triggerList){
18179 this.loadNext = false;
18181 if(this.isExpanded()){
18183 if (!this.blockFocus) {
18184 this.inputEl().focus();
18188 this.hasFocus = true;
18189 if(this.triggerAction == 'all') {
18190 this.doQuery(this.allQuery, true);
18192 this.doQuery(this.getRawValue());
18194 if (!this.blockFocus) {
18195 this.inputEl().focus();
18200 onTickableTriggerClick : function(e)
18207 this.loadNext = false;
18208 this.hasFocus = true;
18210 if(this.triggerAction == 'all') {
18211 this.doQuery(this.allQuery, true);
18213 this.doQuery(this.getRawValue());
18217 onSearchFieldClick : function(e)
18219 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18220 this.onTickableFooterButtonClick(e, false, false);
18224 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18229 this.loadNext = false;
18230 this.hasFocus = true;
18232 if(this.triggerAction == 'all') {
18233 this.doQuery(this.allQuery, true);
18235 this.doQuery(this.getRawValue());
18239 listKeyPress : function(e)
18241 //Roo.log('listkeypress');
18242 // scroll to first matching element based on key pres..
18243 if (e.isSpecialKey()) {
18246 var k = String.fromCharCode(e.getKey()).toUpperCase();
18249 var csel = this.view.getSelectedNodes();
18250 var cselitem = false;
18252 var ix = this.view.indexOf(csel[0]);
18253 cselitem = this.store.getAt(ix);
18254 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18260 this.store.each(function(v) {
18262 // start at existing selection.
18263 if (cselitem.id == v.id) {
18269 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18270 match = this.store.indexOf(v);
18276 if (match === false) {
18277 return true; // no more action?
18280 this.view.select(match);
18281 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18282 sn.scrollIntoView(sn.dom.parentNode, false);
18285 onViewScroll : function(e, t){
18287 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){
18291 this.hasQuery = true;
18293 this.loading = this.list.select('.loading', true).first();
18295 if(this.loading === null){
18296 this.list.createChild({
18298 cls: 'loading roo-select2-more-results roo-select2-active',
18299 html: 'Loading more results...'
18302 this.loading = this.list.select('.loading', true).first();
18304 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18306 this.loading.hide();
18309 this.loading.show();
18314 this.loadNext = true;
18316 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18321 addItem : function(o)
18323 var dv = ''; // display value
18325 if (this.displayField) {
18326 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18328 // this is an error condition!!!
18329 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18336 var choice = this.choices.createChild({
18338 cls: 'roo-select2-search-choice',
18347 cls: 'roo-select2-search-choice-close fa fa-times',
18352 }, this.searchField);
18354 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18356 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18364 this.inputEl().dom.value = '';
18369 onRemoveItem : function(e, _self, o)
18371 e.preventDefault();
18373 this.lastItem = Roo.apply([], this.item);
18375 var index = this.item.indexOf(o.data) * 1;
18378 Roo.log('not this item?!');
18382 this.item.splice(index, 1);
18387 this.fireEvent('remove', this, e);
18393 syncValue : function()
18395 if(!this.item.length){
18402 Roo.each(this.item, function(i){
18403 if(_this.valueField){
18404 value.push(i[_this.valueField]);
18411 this.value = value.join(',');
18413 if(this.hiddenField){
18414 this.hiddenField.dom.value = this.value;
18417 this.store.fireEvent("datachanged", this.store);
18422 clearItem : function()
18424 if(!this.multiple){
18430 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18438 if(this.tickable && !Roo.isTouch){
18439 this.view.refresh();
18443 inputEl: function ()
18445 if(Roo.isIOS && this.useNativeIOS){
18446 return this.el.select('select.roo-ios-select', true).first();
18449 if(Roo.isTouch && this.mobileTouchView){
18450 return this.el.select('input.form-control',true).first();
18454 return this.searchField;
18457 return this.el.select('input.form-control',true).first();
18460 onTickableFooterButtonClick : function(e, btn, el)
18462 e.preventDefault();
18464 this.lastItem = Roo.apply([], this.item);
18466 if(btn && btn.name == 'cancel'){
18467 this.tickItems = Roo.apply([], this.item);
18476 Roo.each(this.tickItems, function(o){
18484 validate : function()
18486 if(this.getVisibilityEl().hasClass('hidden')){
18490 var v = this.getRawValue();
18493 v = this.getValue();
18496 if(this.disabled || this.allowBlank || v.length){
18501 this.markInvalid();
18505 tickableInputEl : function()
18507 if(!this.tickable || !this.editable){
18508 return this.inputEl();
18511 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18515 getAutoCreateTouchView : function()
18520 cls: 'form-group' //input-group
18526 type : this.inputType,
18527 cls : 'form-control x-combo-noedit',
18528 autocomplete: 'new-password',
18529 placeholder : this.placeholder || '',
18534 input.name = this.name;
18538 input.cls += ' input-' + this.size;
18541 if (this.disabled) {
18542 input.disabled = true;
18546 cls : 'roo-combobox-wrap',
18553 inputblock.cls += ' input-group';
18555 inputblock.cn.unshift({
18557 cls : 'input-group-addon input-group-prepend input-group-text',
18562 if(this.removable && !this.multiple){
18563 inputblock.cls += ' roo-removable';
18565 inputblock.cn.push({
18568 cls : 'roo-combo-removable-btn close'
18572 if(this.hasFeedback && !this.allowBlank){
18574 inputblock.cls += ' has-feedback';
18576 inputblock.cn.push({
18578 cls: 'glyphicon form-control-feedback'
18585 inputblock.cls += (this.before) ? '' : ' input-group';
18587 inputblock.cn.push({
18589 cls : 'input-group-addon input-group-append input-group-text',
18595 var ibwrap = inputblock;
18600 cls: 'roo-select2-choices',
18604 cls: 'roo-select2-search-field',
18617 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18622 cls: 'form-hidden-field'
18628 if(!this.multiple && this.showToggleBtn){
18634 if (this.caret != false) {
18637 cls: 'fa fa-' + this.caret
18644 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18646 Roo.bootstrap.version == 3 ? caret : '',
18649 cls: 'combobox-clear',
18663 combobox.cls += ' roo-select2-container-multi';
18666 var required = this.allowBlank ? {
18668 style: 'display: none'
18671 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18672 tooltip : 'This field is required'
18675 var align = this.labelAlign || this.parentLabelAlign();
18677 if (align ==='left' && this.fieldLabel.length) {
18683 cls : 'control-label col-form-label',
18684 html : this.fieldLabel
18688 cls : 'roo-combobox-wrap ',
18695 var labelCfg = cfg.cn[1];
18696 var contentCfg = cfg.cn[2];
18699 if(this.indicatorpos == 'right'){
18704 cls : 'control-label col-form-label',
18708 html : this.fieldLabel
18714 cls : "roo-combobox-wrap ",
18722 labelCfg = cfg.cn[0];
18723 contentCfg = cfg.cn[1];
18728 if(this.labelWidth > 12){
18729 labelCfg.style = "width: " + this.labelWidth + 'px';
18732 if(this.labelWidth < 13 && this.labelmd == 0){
18733 this.labelmd = this.labelWidth;
18736 if(this.labellg > 0){
18737 labelCfg.cls += ' col-lg-' + this.labellg;
18738 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18741 if(this.labelmd > 0){
18742 labelCfg.cls += ' col-md-' + this.labelmd;
18743 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18746 if(this.labelsm > 0){
18747 labelCfg.cls += ' col-sm-' + this.labelsm;
18748 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18751 if(this.labelxs > 0){
18752 labelCfg.cls += ' col-xs-' + this.labelxs;
18753 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18757 } else if ( this.fieldLabel.length) {
18762 cls : 'control-label',
18763 html : this.fieldLabel
18774 if(this.indicatorpos == 'right'){
18778 cls : 'control-label',
18779 html : this.fieldLabel,
18797 var settings = this;
18799 ['xs','sm','md','lg'].map(function(size){
18800 if (settings[size]) {
18801 cfg.cls += ' col-' + size + '-' + settings[size];
18808 initTouchView : function()
18810 this.renderTouchView();
18812 this.touchViewEl.on('scroll', function(){
18813 this.el.dom.scrollTop = 0;
18816 this.originalValue = this.getValue();
18818 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18820 this.inputEl().on("click", this.showTouchView, this);
18821 if (this.triggerEl) {
18822 this.triggerEl.on("click", this.showTouchView, this);
18826 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18827 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18829 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18831 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18832 this.store.on('load', this.onTouchViewLoad, this);
18833 this.store.on('loadexception', this.onTouchViewLoadException, this);
18835 if(this.hiddenName){
18837 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18839 this.hiddenField.dom.value =
18840 this.hiddenValue !== undefined ? this.hiddenValue :
18841 this.value !== undefined ? this.value : '';
18843 this.el.dom.removeAttribute('name');
18844 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18848 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18849 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18852 if(this.removable && !this.multiple){
18853 var close = this.closeTriggerEl();
18855 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18856 close.on('click', this.removeBtnClick, this, close);
18860 * fix the bug in Safari iOS8
18862 this.inputEl().on("focus", function(e){
18863 document.activeElement.blur();
18866 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18873 renderTouchView : function()
18875 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18876 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18878 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18879 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18881 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18882 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18883 this.touchViewBodyEl.setStyle('overflow', 'auto');
18885 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18886 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18888 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18889 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18893 showTouchView : function()
18899 this.touchViewHeaderEl.hide();
18901 if(this.modalTitle.length){
18902 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18903 this.touchViewHeaderEl.show();
18906 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18907 this.touchViewEl.show();
18909 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18911 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18912 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18914 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18916 if(this.modalTitle.length){
18917 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18920 this.touchViewBodyEl.setHeight(bodyHeight);
18924 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18926 this.touchViewEl.addClass(['in','show']);
18929 if(this._touchViewMask){
18930 Roo.get(document.body).addClass("x-body-masked");
18931 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18932 this._touchViewMask.setStyle('z-index', 10000);
18933 this._touchViewMask.addClass('show');
18936 this.doTouchViewQuery();
18940 hideTouchView : function()
18942 this.touchViewEl.removeClass(['in','show']);
18946 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18948 this.touchViewEl.setStyle('display', 'none');
18951 if(this._touchViewMask){
18952 this._touchViewMask.removeClass('show');
18953 Roo.get(document.body).removeClass("x-body-masked");
18957 setTouchViewValue : function()
18964 Roo.each(this.tickItems, function(o){
18969 this.hideTouchView();
18972 doTouchViewQuery : function()
18981 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18985 if(!this.alwaysQuery || this.mode == 'local'){
18986 this.onTouchViewLoad();
18993 onTouchViewBeforeLoad : function(combo,opts)
18999 onTouchViewLoad : function()
19001 if(this.store.getCount() < 1){
19002 this.onTouchViewEmptyResults();
19006 this.clearTouchView();
19008 var rawValue = this.getRawValue();
19010 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19012 this.tickItems = [];
19014 this.store.data.each(function(d, rowIndex){
19015 var row = this.touchViewListGroup.createChild(template);
19017 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19018 row.addClass(d.data.cls);
19021 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19024 html : d.data[this.displayField]
19027 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19028 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19031 row.removeClass('selected');
19032 if(!this.multiple && this.valueField &&
19033 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19036 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19037 row.addClass('selected');
19040 if(this.multiple && this.valueField &&
19041 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19045 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19046 this.tickItems.push(d.data);
19049 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19053 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19055 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19057 if(this.modalTitle.length){
19058 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19061 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19063 if(this.mobile_restrict_height && listHeight < bodyHeight){
19064 this.touchViewBodyEl.setHeight(listHeight);
19069 if(firstChecked && listHeight > bodyHeight){
19070 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19075 onTouchViewLoadException : function()
19077 this.hideTouchView();
19080 onTouchViewEmptyResults : function()
19082 this.clearTouchView();
19084 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19086 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19090 clearTouchView : function()
19092 this.touchViewListGroup.dom.innerHTML = '';
19095 onTouchViewClick : function(e, el, o)
19097 e.preventDefault();
19100 var rowIndex = o.rowIndex;
19102 var r = this.store.getAt(rowIndex);
19104 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19106 if(!this.multiple){
19107 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19108 c.dom.removeAttribute('checked');
19111 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19113 this.setFromData(r.data);
19115 var close = this.closeTriggerEl();
19121 this.hideTouchView();
19123 this.fireEvent('select', this, r, rowIndex);
19128 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19129 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19130 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19134 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19135 this.addItem(r.data);
19136 this.tickItems.push(r.data);
19140 getAutoCreateNativeIOS : function()
19143 cls: 'form-group' //input-group,
19148 cls : 'roo-ios-select'
19152 combobox.name = this.name;
19155 if (this.disabled) {
19156 combobox.disabled = true;
19159 var settings = this;
19161 ['xs','sm','md','lg'].map(function(size){
19162 if (settings[size]) {
19163 cfg.cls += ' col-' + size + '-' + settings[size];
19173 initIOSView : function()
19175 this.store.on('load', this.onIOSViewLoad, this);
19180 onIOSViewLoad : function()
19182 if(this.store.getCount() < 1){
19186 this.clearIOSView();
19188 if(this.allowBlank) {
19190 var default_text = '-- SELECT --';
19192 if(this.placeholder.length){
19193 default_text = this.placeholder;
19196 if(this.emptyTitle.length){
19197 default_text += ' - ' + this.emptyTitle + ' -';
19200 var opt = this.inputEl().createChild({
19203 html : default_text
19207 o[this.valueField] = 0;
19208 o[this.displayField] = default_text;
19210 this.ios_options.push({
19217 this.store.data.each(function(d, rowIndex){
19221 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19222 html = d.data[this.displayField];
19227 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19228 value = d.data[this.valueField];
19237 if(this.value == d.data[this.valueField]){
19238 option['selected'] = true;
19241 var opt = this.inputEl().createChild(option);
19243 this.ios_options.push({
19250 this.inputEl().on('change', function(){
19251 this.fireEvent('select', this);
19256 clearIOSView: function()
19258 this.inputEl().dom.innerHTML = '';
19260 this.ios_options = [];
19263 setIOSValue: function(v)
19267 if(!this.ios_options){
19271 Roo.each(this.ios_options, function(opts){
19273 opts.el.dom.removeAttribute('selected');
19275 if(opts.data[this.valueField] != v){
19279 opts.el.dom.setAttribute('selected', true);
19285 * @cfg {Boolean} grow
19289 * @cfg {Number} growMin
19293 * @cfg {Number} growMax
19302 Roo.apply(Roo.bootstrap.ComboBox, {
19306 cls: 'modal-header',
19328 cls: 'list-group-item',
19332 cls: 'roo-combobox-list-group-item-value'
19336 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19350 listItemCheckbox : {
19352 cls: 'list-group-item',
19356 cls: 'roo-combobox-list-group-item-value'
19360 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19376 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19381 cls: 'modal-footer',
19389 cls: 'col-xs-6 text-left',
19392 cls: 'btn btn-danger roo-touch-view-cancel',
19398 cls: 'col-xs-6 text-right',
19401 cls: 'btn btn-success roo-touch-view-ok',
19412 Roo.apply(Roo.bootstrap.ComboBox, {
19414 touchViewTemplate : {
19416 cls: 'modal fade roo-combobox-touch-view',
19420 cls: 'modal-dialog',
19421 style : 'position:fixed', // we have to fix position....
19425 cls: 'modal-content',
19427 Roo.bootstrap.ComboBox.header,
19428 Roo.bootstrap.ComboBox.body,
19429 Roo.bootstrap.ComboBox.footer
19438 * Ext JS Library 1.1.1
19439 * Copyright(c) 2006-2007, Ext JS, LLC.
19441 * Originally Released Under LGPL - original licence link has changed is not relivant.
19444 * <script type="text/javascript">
19449 * @extends Roo.util.Observable
19450 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19451 * This class also supports single and multi selection modes. <br>
19452 * Create a data model bound view:
19454 var store = new Roo.data.Store(...);
19456 var view = new Roo.View({
19458 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19460 singleSelect: true,
19461 selectedClass: "ydataview-selected",
19465 // listen for node click?
19466 view.on("click", function(vw, index, node, e){
19467 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19471 dataModel.load("foobar.xml");
19473 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19475 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19476 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19478 * Note: old style constructor is still suported (container, template, config)
19481 * Create a new View
19482 * @param {Object} config The config object
19485 Roo.View = function(config, depreciated_tpl, depreciated_config){
19487 this.parent = false;
19489 if (typeof(depreciated_tpl) == 'undefined') {
19490 // new way.. - universal constructor.
19491 Roo.apply(this, config);
19492 this.el = Roo.get(this.el);
19495 this.el = Roo.get(config);
19496 this.tpl = depreciated_tpl;
19497 Roo.apply(this, depreciated_config);
19499 this.wrapEl = this.el.wrap().wrap();
19500 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19503 if(typeof(this.tpl) == "string"){
19504 this.tpl = new Roo.Template(this.tpl);
19506 // support xtype ctors..
19507 this.tpl = new Roo.factory(this.tpl, Roo);
19511 this.tpl.compile();
19516 * @event beforeclick
19517 * Fires before a click is processed. Returns false to cancel the default action.
19518 * @param {Roo.View} this
19519 * @param {Number} index The index of the target node
19520 * @param {HTMLElement} node The target node
19521 * @param {Roo.EventObject} e The raw event object
19523 "beforeclick" : true,
19526 * Fires when a template node is clicked.
19527 * @param {Roo.View} this
19528 * @param {Number} index The index of the target node
19529 * @param {HTMLElement} node The target node
19530 * @param {Roo.EventObject} e The raw event object
19535 * Fires when a template node is double clicked.
19536 * @param {Roo.View} this
19537 * @param {Number} index The index of the target node
19538 * @param {HTMLElement} node The target node
19539 * @param {Roo.EventObject} e The raw event object
19543 * @event contextmenu
19544 * Fires when a template node is right clicked.
19545 * @param {Roo.View} this
19546 * @param {Number} index The index of the target node
19547 * @param {HTMLElement} node The target node
19548 * @param {Roo.EventObject} e The raw event object
19550 "contextmenu" : true,
19552 * @event selectionchange
19553 * Fires when the selected nodes change.
19554 * @param {Roo.View} this
19555 * @param {Array} selections Array of the selected nodes
19557 "selectionchange" : true,
19560 * @event beforeselect
19561 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19562 * @param {Roo.View} this
19563 * @param {HTMLElement} node The node to be selected
19564 * @param {Array} selections Array of currently selected nodes
19566 "beforeselect" : true,
19568 * @event preparedata
19569 * Fires on every row to render, to allow you to change the data.
19570 * @param {Roo.View} this
19571 * @param {Object} data to be rendered (change this)
19573 "preparedata" : true
19581 "click": this.onClick,
19582 "dblclick": this.onDblClick,
19583 "contextmenu": this.onContextMenu,
19587 this.selections = [];
19589 this.cmp = new Roo.CompositeElementLite([]);
19591 this.store = Roo.factory(this.store, Roo.data);
19592 this.setStore(this.store, true);
19595 if ( this.footer && this.footer.xtype) {
19597 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19599 this.footer.dataSource = this.store;
19600 this.footer.container = fctr;
19601 this.footer = Roo.factory(this.footer, Roo);
19602 fctr.insertFirst(this.el);
19604 // this is a bit insane - as the paging toolbar seems to detach the el..
19605 // dom.parentNode.parentNode.parentNode
19606 // they get detached?
19610 Roo.View.superclass.constructor.call(this);
19615 Roo.extend(Roo.View, Roo.util.Observable, {
19618 * @cfg {Roo.data.Store} store Data store to load data from.
19623 * @cfg {String|Roo.Element} el The container element.
19628 * @cfg {String|Roo.Template} tpl The template used by this View
19632 * @cfg {String} dataName the named area of the template to use as the data area
19633 * Works with domtemplates roo-name="name"
19637 * @cfg {String} selectedClass The css class to add to selected nodes
19639 selectedClass : "x-view-selected",
19641 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19646 * @cfg {String} text to display on mask (default Loading)
19650 * @cfg {Boolean} multiSelect Allow multiple selection
19652 multiSelect : false,
19654 * @cfg {Boolean} singleSelect Allow single selection
19656 singleSelect: false,
19659 * @cfg {Boolean} toggleSelect - selecting
19661 toggleSelect : false,
19664 * @cfg {Boolean} tickable - selecting
19669 * Returns the element this view is bound to.
19670 * @return {Roo.Element}
19672 getEl : function(){
19673 return this.wrapEl;
19679 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19681 refresh : function(){
19682 //Roo.log('refresh');
19685 // if we are using something like 'domtemplate', then
19686 // the what gets used is:
19687 // t.applySubtemplate(NAME, data, wrapping data..)
19688 // the outer template then get' applied with
19689 // the store 'extra data'
19690 // and the body get's added to the
19691 // roo-name="data" node?
19692 // <span class='roo-tpl-{name}'></span> ?????
19696 this.clearSelections();
19697 this.el.update("");
19699 var records = this.store.getRange();
19700 if(records.length < 1) {
19702 // is this valid?? = should it render a template??
19704 this.el.update(this.emptyText);
19708 if (this.dataName) {
19709 this.el.update(t.apply(this.store.meta)); //????
19710 el = this.el.child('.roo-tpl-' + this.dataName);
19713 for(var i = 0, len = records.length; i < len; i++){
19714 var data = this.prepareData(records[i].data, i, records[i]);
19715 this.fireEvent("preparedata", this, data, i, records[i]);
19717 var d = Roo.apply({}, data);
19720 Roo.apply(d, {'roo-id' : Roo.id()});
19724 Roo.each(this.parent.item, function(item){
19725 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19728 Roo.apply(d, {'roo-data-checked' : 'checked'});
19732 html[html.length] = Roo.util.Format.trim(
19734 t.applySubtemplate(this.dataName, d, this.store.meta) :
19741 el.update(html.join(""));
19742 this.nodes = el.dom.childNodes;
19743 this.updateIndexes(0);
19748 * Function to override to reformat the data that is sent to
19749 * the template for each node.
19750 * DEPRICATED - use the preparedata event handler.
19751 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19752 * a JSON object for an UpdateManager bound view).
19754 prepareData : function(data, index, record)
19756 this.fireEvent("preparedata", this, data, index, record);
19760 onUpdate : function(ds, record){
19761 // Roo.log('on update');
19762 this.clearSelections();
19763 var index = this.store.indexOf(record);
19764 var n = this.nodes[index];
19765 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19766 n.parentNode.removeChild(n);
19767 this.updateIndexes(index, index);
19773 onAdd : function(ds, records, index)
19775 //Roo.log(['on Add', ds, records, index] );
19776 this.clearSelections();
19777 if(this.nodes.length == 0){
19781 var n = this.nodes[index];
19782 for(var i = 0, len = records.length; i < len; i++){
19783 var d = this.prepareData(records[i].data, i, records[i]);
19785 this.tpl.insertBefore(n, d);
19788 this.tpl.append(this.el, d);
19791 this.updateIndexes(index);
19794 onRemove : function(ds, record, index){
19795 // Roo.log('onRemove');
19796 this.clearSelections();
19797 var el = this.dataName ?
19798 this.el.child('.roo-tpl-' + this.dataName) :
19801 el.dom.removeChild(this.nodes[index]);
19802 this.updateIndexes(index);
19806 * Refresh an individual node.
19807 * @param {Number} index
19809 refreshNode : function(index){
19810 this.onUpdate(this.store, this.store.getAt(index));
19813 updateIndexes : function(startIndex, endIndex){
19814 var ns = this.nodes;
19815 startIndex = startIndex || 0;
19816 endIndex = endIndex || ns.length - 1;
19817 for(var i = startIndex; i <= endIndex; i++){
19818 ns[i].nodeIndex = i;
19823 * Changes the data store this view uses and refresh the view.
19824 * @param {Store} store
19826 setStore : function(store, initial){
19827 if(!initial && this.store){
19828 this.store.un("datachanged", this.refresh);
19829 this.store.un("add", this.onAdd);
19830 this.store.un("remove", this.onRemove);
19831 this.store.un("update", this.onUpdate);
19832 this.store.un("clear", this.refresh);
19833 this.store.un("beforeload", this.onBeforeLoad);
19834 this.store.un("load", this.onLoad);
19835 this.store.un("loadexception", this.onLoad);
19839 store.on("datachanged", this.refresh, this);
19840 store.on("add", this.onAdd, this);
19841 store.on("remove", this.onRemove, this);
19842 store.on("update", this.onUpdate, this);
19843 store.on("clear", this.refresh, this);
19844 store.on("beforeload", this.onBeforeLoad, this);
19845 store.on("load", this.onLoad, this);
19846 store.on("loadexception", this.onLoad, this);
19854 * onbeforeLoad - masks the loading area.
19857 onBeforeLoad : function(store,opts)
19859 //Roo.log('onBeforeLoad');
19861 this.el.update("");
19863 this.el.mask(this.mask ? this.mask : "Loading" );
19865 onLoad : function ()
19872 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19873 * @param {HTMLElement} node
19874 * @return {HTMLElement} The template node
19876 findItemFromChild : function(node){
19877 var el = this.dataName ?
19878 this.el.child('.roo-tpl-' + this.dataName,true) :
19881 if(!node || node.parentNode == el){
19884 var p = node.parentNode;
19885 while(p && p != el){
19886 if(p.parentNode == el){
19895 onClick : function(e){
19896 var item = this.findItemFromChild(e.getTarget());
19898 var index = this.indexOf(item);
19899 if(this.onItemClick(item, index, e) !== false){
19900 this.fireEvent("click", this, index, item, e);
19903 this.clearSelections();
19908 onContextMenu : function(e){
19909 var item = this.findItemFromChild(e.getTarget());
19911 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19916 onDblClick : function(e){
19917 var item = this.findItemFromChild(e.getTarget());
19919 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19923 onItemClick : function(item, index, e)
19925 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19928 if (this.toggleSelect) {
19929 var m = this.isSelected(item) ? 'unselect' : 'select';
19932 _t[m](item, true, false);
19935 if(this.multiSelect || this.singleSelect){
19936 if(this.multiSelect && e.shiftKey && this.lastSelection){
19937 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19939 this.select(item, this.multiSelect && e.ctrlKey);
19940 this.lastSelection = item;
19943 if(!this.tickable){
19944 e.preventDefault();
19952 * Get the number of selected nodes.
19955 getSelectionCount : function(){
19956 return this.selections.length;
19960 * Get the currently selected nodes.
19961 * @return {Array} An array of HTMLElements
19963 getSelectedNodes : function(){
19964 return this.selections;
19968 * Get the indexes of the selected nodes.
19971 getSelectedIndexes : function(){
19972 var indexes = [], s = this.selections;
19973 for(var i = 0, len = s.length; i < len; i++){
19974 indexes.push(s[i].nodeIndex);
19980 * Clear all selections
19981 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19983 clearSelections : function(suppressEvent){
19984 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19985 this.cmp.elements = this.selections;
19986 this.cmp.removeClass(this.selectedClass);
19987 this.selections = [];
19988 if(!suppressEvent){
19989 this.fireEvent("selectionchange", this, this.selections);
19995 * Returns true if the passed node is selected
19996 * @param {HTMLElement/Number} node The node or node index
19997 * @return {Boolean}
19999 isSelected : function(node){
20000 var s = this.selections;
20004 node = this.getNode(node);
20005 return s.indexOf(node) !== -1;
20010 * @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
20011 * @param {Boolean} keepExisting (optional) true to keep existing selections
20012 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20014 select : function(nodeInfo, keepExisting, suppressEvent){
20015 if(nodeInfo instanceof Array){
20017 this.clearSelections(true);
20019 for(var i = 0, len = nodeInfo.length; i < len; i++){
20020 this.select(nodeInfo[i], true, true);
20024 var node = this.getNode(nodeInfo);
20025 if(!node || this.isSelected(node)){
20026 return; // already selected.
20029 this.clearSelections(true);
20032 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20033 Roo.fly(node).addClass(this.selectedClass);
20034 this.selections.push(node);
20035 if(!suppressEvent){
20036 this.fireEvent("selectionchange", this, this.selections);
20044 * @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
20045 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20046 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20048 unselect : function(nodeInfo, keepExisting, suppressEvent)
20050 if(nodeInfo instanceof Array){
20051 Roo.each(this.selections, function(s) {
20052 this.unselect(s, nodeInfo);
20056 var node = this.getNode(nodeInfo);
20057 if(!node || !this.isSelected(node)){
20058 //Roo.log("not selected");
20059 return; // not selected.
20063 Roo.each(this.selections, function(s) {
20065 Roo.fly(node).removeClass(this.selectedClass);
20072 this.selections= ns;
20073 this.fireEvent("selectionchange", this, this.selections);
20077 * Gets a template node.
20078 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20079 * @return {HTMLElement} The node or null if it wasn't found
20081 getNode : function(nodeInfo){
20082 if(typeof nodeInfo == "string"){
20083 return document.getElementById(nodeInfo);
20084 }else if(typeof nodeInfo == "number"){
20085 return this.nodes[nodeInfo];
20091 * Gets a range template nodes.
20092 * @param {Number} startIndex
20093 * @param {Number} endIndex
20094 * @return {Array} An array of nodes
20096 getNodes : function(start, end){
20097 var ns = this.nodes;
20098 start = start || 0;
20099 end = typeof end == "undefined" ? ns.length - 1 : end;
20102 for(var i = start; i <= end; i++){
20106 for(var i = start; i >= end; i--){
20114 * Finds the index of the passed node
20115 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20116 * @return {Number} The index of the node or -1
20118 indexOf : function(node){
20119 node = this.getNode(node);
20120 if(typeof node.nodeIndex == "number"){
20121 return node.nodeIndex;
20123 var ns = this.nodes;
20124 for(var i = 0, len = ns.length; i < len; i++){
20135 * based on jquery fullcalendar
20139 Roo.bootstrap = Roo.bootstrap || {};
20141 * @class Roo.bootstrap.Calendar
20142 * @extends Roo.bootstrap.Component
20143 * Bootstrap Calendar class
20144 * @cfg {Boolean} loadMask (true|false) default false
20145 * @cfg {Object} header generate the user specific header of the calendar, default false
20148 * Create a new Container
20149 * @param {Object} config The config object
20154 Roo.bootstrap.Calendar = function(config){
20155 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20159 * Fires when a date is selected
20160 * @param {DatePicker} this
20161 * @param {Date} date The selected date
20165 * @event monthchange
20166 * Fires when the displayed month changes
20167 * @param {DatePicker} this
20168 * @param {Date} date The selected month
20170 'monthchange': true,
20172 * @event evententer
20173 * Fires when mouse over an event
20174 * @param {Calendar} this
20175 * @param {event} Event
20177 'evententer': true,
20179 * @event eventleave
20180 * Fires when the mouse leaves an
20181 * @param {Calendar} this
20184 'eventleave': true,
20186 * @event eventclick
20187 * Fires when the mouse click an
20188 * @param {Calendar} this
20197 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20200 * @cfg {Number} startDay
20201 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20209 getAutoCreate : function(){
20212 var fc_button = function(name, corner, style, content ) {
20213 return Roo.apply({},{
20215 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20217 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20220 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20231 style : 'width:100%',
20238 cls : 'fc-header-left',
20240 fc_button('prev', 'left', 'arrow', '‹' ),
20241 fc_button('next', 'right', 'arrow', '›' ),
20242 { tag: 'span', cls: 'fc-header-space' },
20243 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20251 cls : 'fc-header-center',
20255 cls: 'fc-header-title',
20258 html : 'month / year'
20266 cls : 'fc-header-right',
20268 /* fc_button('month', 'left', '', 'month' ),
20269 fc_button('week', '', '', 'week' ),
20270 fc_button('day', 'right', '', 'day' )
20282 header = this.header;
20285 var cal_heads = function() {
20287 // fixme - handle this.
20289 for (var i =0; i < Date.dayNames.length; i++) {
20290 var d = Date.dayNames[i];
20293 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20294 html : d.substring(0,3)
20298 ret[0].cls += ' fc-first';
20299 ret[6].cls += ' fc-last';
20302 var cal_cell = function(n) {
20305 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20310 cls: 'fc-day-number',
20314 cls: 'fc-day-content',
20318 style: 'position: relative;' // height: 17px;
20330 var cal_rows = function() {
20333 for (var r = 0; r < 6; r++) {
20340 for (var i =0; i < Date.dayNames.length; i++) {
20341 var d = Date.dayNames[i];
20342 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20345 row.cn[0].cls+=' fc-first';
20346 row.cn[0].cn[0].style = 'min-height:90px';
20347 row.cn[6].cls+=' fc-last';
20351 ret[0].cls += ' fc-first';
20352 ret[4].cls += ' fc-prev-last';
20353 ret[5].cls += ' fc-last';
20360 cls: 'fc-border-separate',
20361 style : 'width:100%',
20369 cls : 'fc-first fc-last',
20387 cls : 'fc-content',
20388 style : "position: relative;",
20391 cls : 'fc-view fc-view-month fc-grid',
20392 style : 'position: relative',
20393 unselectable : 'on',
20396 cls : 'fc-event-container',
20397 style : 'position:absolute;z-index:8;top:0;left:0;'
20415 initEvents : function()
20418 throw "can not find store for calendar";
20424 style: "text-align:center",
20428 style: "background-color:white;width:50%;margin:250 auto",
20432 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20443 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20445 var size = this.el.select('.fc-content', true).first().getSize();
20446 this.maskEl.setSize(size.width, size.height);
20447 this.maskEl.enableDisplayMode("block");
20448 if(!this.loadMask){
20449 this.maskEl.hide();
20452 this.store = Roo.factory(this.store, Roo.data);
20453 this.store.on('load', this.onLoad, this);
20454 this.store.on('beforeload', this.onBeforeLoad, this);
20458 this.cells = this.el.select('.fc-day',true);
20459 //Roo.log(this.cells);
20460 this.textNodes = this.el.query('.fc-day-number');
20461 this.cells.addClassOnOver('fc-state-hover');
20463 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20464 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20465 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20466 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20468 this.on('monthchange', this.onMonthChange, this);
20470 this.update(new Date().clearTime());
20473 resize : function() {
20474 var sz = this.el.getSize();
20476 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20477 this.el.select('.fc-day-content div',true).setHeight(34);
20482 showPrevMonth : function(e){
20483 this.update(this.activeDate.add("mo", -1));
20485 showToday : function(e){
20486 this.update(new Date().clearTime());
20489 showNextMonth : function(e){
20490 this.update(this.activeDate.add("mo", 1));
20494 showPrevYear : function(){
20495 this.update(this.activeDate.add("y", -1));
20499 showNextYear : function(){
20500 this.update(this.activeDate.add("y", 1));
20505 update : function(date)
20507 var vd = this.activeDate;
20508 this.activeDate = date;
20509 // if(vd && this.el){
20510 // var t = date.getTime();
20511 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20512 // Roo.log('using add remove');
20514 // this.fireEvent('monthchange', this, date);
20516 // this.cells.removeClass("fc-state-highlight");
20517 // this.cells.each(function(c){
20518 // if(c.dateValue == t){
20519 // c.addClass("fc-state-highlight");
20520 // setTimeout(function(){
20521 // try{c.dom.firstChild.focus();}catch(e){}
20531 var days = date.getDaysInMonth();
20533 var firstOfMonth = date.getFirstDateOfMonth();
20534 var startingPos = firstOfMonth.getDay()-this.startDay;
20536 if(startingPos < this.startDay){
20540 var pm = date.add(Date.MONTH, -1);
20541 var prevStart = pm.getDaysInMonth()-startingPos;
20543 this.cells = this.el.select('.fc-day',true);
20544 this.textNodes = this.el.query('.fc-day-number');
20545 this.cells.addClassOnOver('fc-state-hover');
20547 var cells = this.cells.elements;
20548 var textEls = this.textNodes;
20550 Roo.each(cells, function(cell){
20551 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20554 days += startingPos;
20556 // convert everything to numbers so it's fast
20557 var day = 86400000;
20558 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20561 //Roo.log(prevStart);
20563 var today = new Date().clearTime().getTime();
20564 var sel = date.clearTime().getTime();
20565 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20566 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20567 var ddMatch = this.disabledDatesRE;
20568 var ddText = this.disabledDatesText;
20569 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20570 var ddaysText = this.disabledDaysText;
20571 var format = this.format;
20573 var setCellClass = function(cal, cell){
20577 //Roo.log('set Cell Class');
20579 var t = d.getTime();
20583 cell.dateValue = t;
20585 cell.className += " fc-today";
20586 cell.className += " fc-state-highlight";
20587 cell.title = cal.todayText;
20590 // disable highlight in other month..
20591 //cell.className += " fc-state-highlight";
20596 cell.className = " fc-state-disabled";
20597 cell.title = cal.minText;
20601 cell.className = " fc-state-disabled";
20602 cell.title = cal.maxText;
20606 if(ddays.indexOf(d.getDay()) != -1){
20607 cell.title = ddaysText;
20608 cell.className = " fc-state-disabled";
20611 if(ddMatch && format){
20612 var fvalue = d.dateFormat(format);
20613 if(ddMatch.test(fvalue)){
20614 cell.title = ddText.replace("%0", fvalue);
20615 cell.className = " fc-state-disabled";
20619 if (!cell.initialClassName) {
20620 cell.initialClassName = cell.dom.className;
20623 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20628 for(; i < startingPos; i++) {
20629 textEls[i].innerHTML = (++prevStart);
20630 d.setDate(d.getDate()+1);
20632 cells[i].className = "fc-past fc-other-month";
20633 setCellClass(this, cells[i]);
20638 for(; i < days; i++){
20639 intDay = i - startingPos + 1;
20640 textEls[i].innerHTML = (intDay);
20641 d.setDate(d.getDate()+1);
20643 cells[i].className = ''; // "x-date-active";
20644 setCellClass(this, cells[i]);
20648 for(; i < 42; i++) {
20649 textEls[i].innerHTML = (++extraDays);
20650 d.setDate(d.getDate()+1);
20652 cells[i].className = "fc-future fc-other-month";
20653 setCellClass(this, cells[i]);
20656 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20658 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20660 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20661 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20663 if(totalRows != 6){
20664 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20665 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20668 this.fireEvent('monthchange', this, date);
20672 if(!this.internalRender){
20673 var main = this.el.dom.firstChild;
20674 var w = main.offsetWidth;
20675 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20676 Roo.fly(main).setWidth(w);
20677 this.internalRender = true;
20678 // opera does not respect the auto grow header center column
20679 // then, after it gets a width opera refuses to recalculate
20680 // without a second pass
20681 if(Roo.isOpera && !this.secondPass){
20682 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20683 this.secondPass = true;
20684 this.update.defer(10, this, [date]);
20691 findCell : function(dt) {
20692 dt = dt.clearTime().getTime();
20694 this.cells.each(function(c){
20695 //Roo.log("check " +c.dateValue + '?=' + dt);
20696 if(c.dateValue == dt){
20706 findCells : function(ev) {
20707 var s = ev.start.clone().clearTime().getTime();
20709 var e= ev.end.clone().clearTime().getTime();
20712 this.cells.each(function(c){
20713 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20715 if(c.dateValue > e){
20718 if(c.dateValue < s){
20727 // findBestRow: function(cells)
20731 // for (var i =0 ; i < cells.length;i++) {
20732 // ret = Math.max(cells[i].rows || 0,ret);
20739 addItem : function(ev)
20741 // look for vertical location slot in
20742 var cells = this.findCells(ev);
20744 // ev.row = this.findBestRow(cells);
20746 // work out the location.
20750 for(var i =0; i < cells.length; i++) {
20752 cells[i].row = cells[0].row;
20755 cells[i].row = cells[i].row + 1;
20765 if (crow.start.getY() == cells[i].getY()) {
20767 crow.end = cells[i];
20784 cells[0].events.push(ev);
20786 this.calevents.push(ev);
20789 clearEvents: function() {
20791 if(!this.calevents){
20795 Roo.each(this.cells.elements, function(c){
20801 Roo.each(this.calevents, function(e) {
20802 Roo.each(e.els, function(el) {
20803 el.un('mouseenter' ,this.onEventEnter, this);
20804 el.un('mouseleave' ,this.onEventLeave, this);
20809 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20815 renderEvents: function()
20819 this.cells.each(function(c) {
20828 if(c.row != c.events.length){
20829 r = 4 - (4 - (c.row - c.events.length));
20832 c.events = ev.slice(0, r);
20833 c.more = ev.slice(r);
20835 if(c.more.length && c.more.length == 1){
20836 c.events.push(c.more.pop());
20839 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20843 this.cells.each(function(c) {
20845 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20848 for (var e = 0; e < c.events.length; e++){
20849 var ev = c.events[e];
20850 var rows = ev.rows;
20852 for(var i = 0; i < rows.length; i++) {
20854 // how many rows should it span..
20857 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20858 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20860 unselectable : "on",
20863 cls: 'fc-event-inner',
20867 // cls: 'fc-event-time',
20868 // html : cells.length > 1 ? '' : ev.time
20872 cls: 'fc-event-title',
20873 html : String.format('{0}', ev.title)
20880 cls: 'ui-resizable-handle ui-resizable-e',
20881 html : '  '
20888 cfg.cls += ' fc-event-start';
20890 if ((i+1) == rows.length) {
20891 cfg.cls += ' fc-event-end';
20894 var ctr = _this.el.select('.fc-event-container',true).first();
20895 var cg = ctr.createChild(cfg);
20897 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20898 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20900 var r = (c.more.length) ? 1 : 0;
20901 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20902 cg.setWidth(ebox.right - sbox.x -2);
20904 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20905 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20906 cg.on('click', _this.onEventClick, _this, ev);
20917 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20918 style : 'position: absolute',
20919 unselectable : "on",
20922 cls: 'fc-event-inner',
20926 cls: 'fc-event-title',
20934 cls: 'ui-resizable-handle ui-resizable-e',
20935 html : '  '
20941 var ctr = _this.el.select('.fc-event-container',true).first();
20942 var cg = ctr.createChild(cfg);
20944 var sbox = c.select('.fc-day-content',true).first().getBox();
20945 var ebox = c.select('.fc-day-content',true).first().getBox();
20947 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20948 cg.setWidth(ebox.right - sbox.x -2);
20950 cg.on('click', _this.onMoreEventClick, _this, c.more);
20960 onEventEnter: function (e, el,event,d) {
20961 this.fireEvent('evententer', this, el, event);
20964 onEventLeave: function (e, el,event,d) {
20965 this.fireEvent('eventleave', this, el, event);
20968 onEventClick: function (e, el,event,d) {
20969 this.fireEvent('eventclick', this, el, event);
20972 onMonthChange: function () {
20976 onMoreEventClick: function(e, el, more)
20980 this.calpopover.placement = 'right';
20981 this.calpopover.setTitle('More');
20983 this.calpopover.setContent('');
20985 var ctr = this.calpopover.el.select('.popover-content', true).first();
20987 Roo.each(more, function(m){
20989 cls : 'fc-event-hori fc-event-draggable',
20992 var cg = ctr.createChild(cfg);
20994 cg.on('click', _this.onEventClick, _this, m);
20997 this.calpopover.show(el);
21002 onLoad: function ()
21004 this.calevents = [];
21007 if(this.store.getCount() > 0){
21008 this.store.data.each(function(d){
21011 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21012 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21013 time : d.data.start_time,
21014 title : d.data.title,
21015 description : d.data.description,
21016 venue : d.data.venue
21021 this.renderEvents();
21023 if(this.calevents.length && this.loadMask){
21024 this.maskEl.hide();
21028 onBeforeLoad: function()
21030 this.clearEvents();
21032 this.maskEl.show();
21046 * @class Roo.bootstrap.Popover
21047 * @extends Roo.bootstrap.Component
21048 * Bootstrap Popover class
21049 * @cfg {String} html contents of the popover (or false to use children..)
21050 * @cfg {String} title of popover (or false to hide)
21051 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21052 * @cfg {String} trigger click || hover (or false to trigger manually)
21053 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21054 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21055 * - if false and it has a 'parent' then it will be automatically added to that element
21056 * - if string - Roo.get will be called
21057 * @cfg {Number} delay - delay before showing
21060 * Create a new Popover
21061 * @param {Object} config The config object
21064 Roo.bootstrap.Popover = function(config){
21065 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21071 * After the popover show
21073 * @param {Roo.bootstrap.Popover} this
21078 * After the popover hide
21080 * @param {Roo.bootstrap.Popover} this
21086 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21091 placement : 'right',
21092 trigger : 'hover', // hover
21098 can_build_overlaid : false,
21100 maskEl : false, // the mask element
21103 alignEl : false, // when show is called with an element - this get's stored.
21105 getChildContainer : function()
21107 return this.contentEl;
21110 getPopoverHeader : function()
21112 this.title = true; // flag not to hide it..
21113 this.headerEl.addClass('p-0');
21114 return this.headerEl
21118 getAutoCreate : function(){
21121 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21122 style: 'display:block',
21128 cls : 'popover-inner ',
21132 cls: 'popover-title popover-header',
21133 html : this.title === false ? '' : this.title
21136 cls : 'popover-content popover-body ' + (this.cls || ''),
21137 html : this.html || ''
21148 * @param {string} the title
21150 setTitle: function(str)
21154 this.headerEl.dom.innerHTML = str;
21159 * @param {string} the body content
21161 setContent: function(str)
21164 if (this.contentEl) {
21165 this.contentEl.dom.innerHTML = str;
21169 // as it get's added to the bottom of the page.
21170 onRender : function(ct, position)
21172 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21177 var cfg = Roo.apply({}, this.getAutoCreate());
21181 cfg.cls += ' ' + this.cls;
21184 cfg.style = this.style;
21186 //Roo.log("adding to ");
21187 this.el = Roo.get(document.body).createChild(cfg, position);
21188 // Roo.log(this.el);
21191 this.contentEl = this.el.select('.popover-content',true).first();
21192 this.headerEl = this.el.select('.popover-title',true).first();
21195 if(typeof(this.items) != 'undefined'){
21196 var items = this.items;
21199 for(var i =0;i < items.length;i++) {
21200 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21204 this.items = nitems;
21206 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21207 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21214 resizeMask : function()
21216 this.maskEl.setSize(
21217 Roo.lib.Dom.getViewWidth(true),
21218 Roo.lib.Dom.getViewHeight(true)
21222 initEvents : function()
21226 Roo.bootstrap.Popover.register(this);
21229 this.arrowEl = this.el.select('.arrow',true).first();
21230 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21231 this.el.enableDisplayMode('block');
21235 if (this.over === false && !this.parent()) {
21238 if (this.triggers === false) {
21243 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21244 var triggers = this.trigger ? this.trigger.split(' ') : [];
21245 Roo.each(triggers, function(trigger) {
21247 if (trigger == 'click') {
21248 on_el.on('click', this.toggle, this);
21249 } else if (trigger != 'manual') {
21250 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21251 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21253 on_el.on(eventIn ,this.enter, this);
21254 on_el.on(eventOut, this.leave, this);
21264 toggle : function () {
21265 this.hoverState == 'in' ? this.leave() : this.enter();
21268 enter : function () {
21270 clearTimeout(this.timeout);
21272 this.hoverState = 'in';
21274 if (!this.delay || !this.delay.show) {
21279 this.timeout = setTimeout(function () {
21280 if (_t.hoverState == 'in') {
21283 }, this.delay.show)
21286 leave : function() {
21287 clearTimeout(this.timeout);
21289 this.hoverState = 'out';
21291 if (!this.delay || !this.delay.hide) {
21296 this.timeout = setTimeout(function () {
21297 if (_t.hoverState == 'out') {
21300 }, this.delay.hide)
21304 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21305 * @param {string} (left|right|top|bottom) position
21307 show : function (on_el, placement)
21309 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21310 on_el = on_el || false; // default to false
21313 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21314 on_el = this.parent().el;
21315 } else if (this.over) {
21316 on_el = Roo.get(this.over);
21321 this.alignEl = Roo.get( on_el );
21324 this.render(document.body);
21330 if (this.title === false) {
21331 this.headerEl.hide();
21336 this.el.dom.style.display = 'block';
21339 if (this.alignEl) {
21340 this.updatePosition(this.placement, true);
21343 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21344 var es = this.el.getSize();
21345 var x = Roo.lib.Dom.getViewWidth()/2;
21346 var y = Roo.lib.Dom.getViewHeight()/2;
21347 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21352 //var arrow = this.el.select('.arrow',true).first();
21353 //arrow.set(align[2],
21355 this.el.addClass('in');
21359 this.hoverState = 'in';
21362 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21363 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21364 this.maskEl.dom.style.display = 'block';
21365 this.maskEl.addClass('show');
21367 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21369 this.fireEvent('show', this);
21373 * fire this manually after loading a grid in the table for example
21374 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21375 * @param {Boolean} try and move it if we cant get right position.
21377 updatePosition : function(placement, try_move)
21379 // allow for calling with no parameters
21380 placement = placement ? placement : this.placement;
21381 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21383 this.el.removeClass([
21384 'fade','top','bottom', 'left', 'right','in',
21385 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21387 this.el.addClass(placement + ' bs-popover-' + placement);
21389 if (!this.alignEl ) {
21393 switch (placement) {
21395 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21396 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21397 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21398 //normal display... or moved up/down.
21399 this.el.setXY(offset);
21400 var xy = this.alignEl.getAnchorXY('tr', false);
21402 this.arrowEl.setXY(xy);
21405 // continue through...
21406 return this.updatePosition('left', false);
21410 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21411 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21412 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21413 //normal display... or moved up/down.
21414 this.el.setXY(offset);
21415 var xy = this.alignEl.getAnchorXY('tl', false);
21416 xy[0]-=10;xy[1]+=5; // << fix me
21417 this.arrowEl.setXY(xy);
21421 return this.updatePosition('right', false);
21424 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21425 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21426 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21427 //normal display... or moved up/down.
21428 this.el.setXY(offset);
21429 var xy = this.alignEl.getAnchorXY('t', false);
21430 xy[1]-=10; // << fix me
21431 this.arrowEl.setXY(xy);
21435 return this.updatePosition('bottom', false);
21438 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21439 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21440 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21441 //normal display... or moved up/down.
21442 this.el.setXY(offset);
21443 var xy = this.alignEl.getAnchorXY('b', false);
21444 xy[1]+=2; // << fix me
21445 this.arrowEl.setXY(xy);
21449 return this.updatePosition('top', false);
21460 this.el.setXY([0,0]);
21461 this.el.removeClass('in');
21463 this.hoverState = null;
21464 this.maskEl.hide(); // always..
21465 this.fireEvent('hide', this);
21471 Roo.apply(Roo.bootstrap.Popover, {
21474 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21475 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21476 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21477 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21482 clickHander : false,
21486 onMouseDown : function(e)
21488 if (this.popups.length && !e.getTarget(".roo-popover")) {
21489 /// what is nothing is showing..
21498 register : function(popup)
21500 if (!Roo.bootstrap.Popover.clickHandler) {
21501 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21503 // hide other popups.
21504 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21505 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21506 this.hideAll(); //<< why?
21507 //this.popups.push(popup);
21509 hideAll : function()
21511 this.popups.forEach(function(p) {
21515 onShow : function() {
21516 Roo.bootstrap.Popover.popups.push(this);
21518 onHide : function() {
21519 Roo.bootstrap.Popover.popups.remove(this);
21525 * Card header - holder for the card header elements.
21530 * @class Roo.bootstrap.PopoverNav
21531 * @extends Roo.bootstrap.NavGroup
21532 * Bootstrap Popover header navigation class
21534 * Create a new Popover Header Navigation
21535 * @param {Object} config The config object
21538 Roo.bootstrap.PopoverNav = function(config){
21539 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21542 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21545 container_method : 'getPopoverHeader'
21563 * @class Roo.bootstrap.Progress
21564 * @extends Roo.bootstrap.Component
21565 * Bootstrap Progress class
21566 * @cfg {Boolean} striped striped of the progress bar
21567 * @cfg {Boolean} active animated of the progress bar
21571 * Create a new Progress
21572 * @param {Object} config The config object
21575 Roo.bootstrap.Progress = function(config){
21576 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21579 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21584 getAutoCreate : function(){
21592 cfg.cls += ' progress-striped';
21596 cfg.cls += ' active';
21615 * @class Roo.bootstrap.ProgressBar
21616 * @extends Roo.bootstrap.Component
21617 * Bootstrap ProgressBar class
21618 * @cfg {Number} aria_valuenow aria-value now
21619 * @cfg {Number} aria_valuemin aria-value min
21620 * @cfg {Number} aria_valuemax aria-value max
21621 * @cfg {String} label label for the progress bar
21622 * @cfg {String} panel (success | info | warning | danger )
21623 * @cfg {String} role role of the progress bar
21624 * @cfg {String} sr_only text
21628 * Create a new ProgressBar
21629 * @param {Object} config The config object
21632 Roo.bootstrap.ProgressBar = function(config){
21633 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21636 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21640 aria_valuemax : 100,
21646 getAutoCreate : function()
21651 cls: 'progress-bar',
21652 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21664 cfg.role = this.role;
21667 if(this.aria_valuenow){
21668 cfg['aria-valuenow'] = this.aria_valuenow;
21671 if(this.aria_valuemin){
21672 cfg['aria-valuemin'] = this.aria_valuemin;
21675 if(this.aria_valuemax){
21676 cfg['aria-valuemax'] = this.aria_valuemax;
21679 if(this.label && !this.sr_only){
21680 cfg.html = this.label;
21684 cfg.cls += ' progress-bar-' + this.panel;
21690 update : function(aria_valuenow)
21692 this.aria_valuenow = aria_valuenow;
21694 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21709 * @class Roo.bootstrap.TabGroup
21710 * @extends Roo.bootstrap.Column
21711 * Bootstrap Column class
21712 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21713 * @cfg {Boolean} carousel true to make the group behave like a carousel
21714 * @cfg {Boolean} bullets show bullets for the panels
21715 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21716 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21717 * @cfg {Boolean} showarrow (true|false) show arrow default true
21720 * Create a new TabGroup
21721 * @param {Object} config The config object
21724 Roo.bootstrap.TabGroup = function(config){
21725 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21727 this.navId = Roo.id();
21730 Roo.bootstrap.TabGroup.register(this);
21734 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21737 transition : false,
21742 slideOnTouch : false,
21745 getAutoCreate : function()
21747 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21749 cfg.cls += ' tab-content';
21751 if (this.carousel) {
21752 cfg.cls += ' carousel slide';
21755 cls : 'carousel-inner',
21759 if(this.bullets && !Roo.isTouch){
21762 cls : 'carousel-bullets',
21766 if(this.bullets_cls){
21767 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21774 cfg.cn[0].cn.push(bullets);
21777 if(this.showarrow){
21778 cfg.cn[0].cn.push({
21780 class : 'carousel-arrow',
21784 class : 'carousel-prev',
21788 class : 'fa fa-chevron-left'
21794 class : 'carousel-next',
21798 class : 'fa fa-chevron-right'
21811 initEvents: function()
21813 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21814 // this.el.on("touchstart", this.onTouchStart, this);
21817 if(this.autoslide){
21820 this.slideFn = window.setInterval(function() {
21821 _this.showPanelNext();
21825 if(this.showarrow){
21826 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21827 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21833 // onTouchStart : function(e, el, o)
21835 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21839 // this.showPanelNext();
21843 getChildContainer : function()
21845 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21849 * register a Navigation item
21850 * @param {Roo.bootstrap.NavItem} the navitem to add
21852 register : function(item)
21854 this.tabs.push( item);
21855 item.navId = this.navId; // not really needed..
21860 getActivePanel : function()
21863 Roo.each(this.tabs, function(t) {
21873 getPanelByName : function(n)
21876 Roo.each(this.tabs, function(t) {
21877 if (t.tabId == n) {
21885 indexOfPanel : function(p)
21888 Roo.each(this.tabs, function(t,i) {
21889 if (t.tabId == p.tabId) {
21898 * show a specific panel
21899 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21900 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21902 showPanel : function (pan)
21904 if(this.transition || typeof(pan) == 'undefined'){
21905 Roo.log("waiting for the transitionend");
21909 if (typeof(pan) == 'number') {
21910 pan = this.tabs[pan];
21913 if (typeof(pan) == 'string') {
21914 pan = this.getPanelByName(pan);
21917 var cur = this.getActivePanel();
21920 Roo.log('pan or acitve pan is undefined');
21924 if (pan.tabId == this.getActivePanel().tabId) {
21928 if (false === cur.fireEvent('beforedeactivate')) {
21932 if(this.bullets > 0 && !Roo.isTouch){
21933 this.setActiveBullet(this.indexOfPanel(pan));
21936 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21938 //class="carousel-item carousel-item-next carousel-item-left"
21940 this.transition = true;
21941 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21942 var lr = dir == 'next' ? 'left' : 'right';
21943 pan.el.addClass(dir); // or prev
21944 pan.el.addClass('carousel-item-' + dir); // or prev
21945 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21946 cur.el.addClass(lr); // or right
21947 pan.el.addClass(lr);
21948 cur.el.addClass('carousel-item-' +lr); // or right
21949 pan.el.addClass('carousel-item-' +lr);
21953 cur.el.on('transitionend', function() {
21954 Roo.log("trans end?");
21956 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21957 pan.setActive(true);
21959 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21960 cur.setActive(false);
21962 _this.transition = false;
21964 }, this, { single: true } );
21969 cur.setActive(false);
21970 pan.setActive(true);
21975 showPanelNext : function()
21977 var i = this.indexOfPanel(this.getActivePanel());
21979 if (i >= this.tabs.length - 1 && !this.autoslide) {
21983 if (i >= this.tabs.length - 1 && this.autoslide) {
21987 this.showPanel(this.tabs[i+1]);
21990 showPanelPrev : function()
21992 var i = this.indexOfPanel(this.getActivePanel());
21994 if (i < 1 && !this.autoslide) {
21998 if (i < 1 && this.autoslide) {
21999 i = this.tabs.length;
22002 this.showPanel(this.tabs[i-1]);
22006 addBullet: function()
22008 if(!this.bullets || Roo.isTouch){
22011 var ctr = this.el.select('.carousel-bullets',true).first();
22012 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22013 var bullet = ctr.createChild({
22014 cls : 'bullet bullet-' + i
22015 },ctr.dom.lastChild);
22020 bullet.on('click', (function(e, el, o, ii, t){
22022 e.preventDefault();
22024 this.showPanel(ii);
22026 if(this.autoslide && this.slideFn){
22027 clearInterval(this.slideFn);
22028 this.slideFn = window.setInterval(function() {
22029 _this.showPanelNext();
22033 }).createDelegate(this, [i, bullet], true));
22038 setActiveBullet : function(i)
22044 Roo.each(this.el.select('.bullet', true).elements, function(el){
22045 el.removeClass('selected');
22048 var bullet = this.el.select('.bullet-' + i, true).first();
22054 bullet.addClass('selected');
22065 Roo.apply(Roo.bootstrap.TabGroup, {
22069 * register a Navigation Group
22070 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22072 register : function(navgrp)
22074 this.groups[navgrp.navId] = navgrp;
22078 * fetch a Navigation Group based on the navigation ID
22079 * if one does not exist , it will get created.
22080 * @param {string} the navgroup to add
22081 * @returns {Roo.bootstrap.NavGroup} the navgroup
22083 get: function(navId) {
22084 if (typeof(this.groups[navId]) == 'undefined') {
22085 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22087 return this.groups[navId] ;
22102 * @class Roo.bootstrap.TabPanel
22103 * @extends Roo.bootstrap.Component
22104 * Bootstrap TabPanel class
22105 * @cfg {Boolean} active panel active
22106 * @cfg {String} html panel content
22107 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22108 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22109 * @cfg {String} href click to link..
22110 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22114 * Create a new TabPanel
22115 * @param {Object} config The config object
22118 Roo.bootstrap.TabPanel = function(config){
22119 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22123 * Fires when the active status changes
22124 * @param {Roo.bootstrap.TabPanel} this
22125 * @param {Boolean} state the new state
22130 * @event beforedeactivate
22131 * Fires before a tab is de-activated - can be used to do validation on a form.
22132 * @param {Roo.bootstrap.TabPanel} this
22133 * @return {Boolean} false if there is an error
22136 'beforedeactivate': true
22139 this.tabId = this.tabId || Roo.id();
22143 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22150 touchSlide : false,
22151 getAutoCreate : function(){
22156 // item is needed for carousel - not sure if it has any effect otherwise
22157 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22158 html: this.html || ''
22162 cfg.cls += ' active';
22166 cfg.tabId = this.tabId;
22174 initEvents: function()
22176 var p = this.parent();
22178 this.navId = this.navId || p.navId;
22180 if (typeof(this.navId) != 'undefined') {
22181 // not really needed.. but just in case.. parent should be a NavGroup.
22182 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22186 var i = tg.tabs.length - 1;
22188 if(this.active && tg.bullets > 0 && i < tg.bullets){
22189 tg.setActiveBullet(i);
22193 this.el.on('click', this.onClick, this);
22195 if(Roo.isTouch && this.touchSlide){
22196 this.el.on("touchstart", this.onTouchStart, this);
22197 this.el.on("touchmove", this.onTouchMove, this);
22198 this.el.on("touchend", this.onTouchEnd, this);
22203 onRender : function(ct, position)
22205 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22208 setActive : function(state)
22210 Roo.log("panel - set active " + this.tabId + "=" + state);
22212 this.active = state;
22214 this.el.removeClass('active');
22216 } else if (!this.el.hasClass('active')) {
22217 this.el.addClass('active');
22220 this.fireEvent('changed', this, state);
22223 onClick : function(e)
22225 e.preventDefault();
22227 if(!this.href.length){
22231 window.location.href = this.href;
22240 onTouchStart : function(e)
22242 this.swiping = false;
22244 this.startX = e.browserEvent.touches[0].clientX;
22245 this.startY = e.browserEvent.touches[0].clientY;
22248 onTouchMove : function(e)
22250 this.swiping = true;
22252 this.endX = e.browserEvent.touches[0].clientX;
22253 this.endY = e.browserEvent.touches[0].clientY;
22256 onTouchEnd : function(e)
22263 var tabGroup = this.parent();
22265 if(this.endX > this.startX){ // swiping right
22266 tabGroup.showPanelPrev();
22270 if(this.startX > this.endX){ // swiping left
22271 tabGroup.showPanelNext();
22290 * @class Roo.bootstrap.DateField
22291 * @extends Roo.bootstrap.Input
22292 * Bootstrap DateField class
22293 * @cfg {Number} weekStart default 0
22294 * @cfg {String} viewMode default empty, (months|years)
22295 * @cfg {String} minViewMode default empty, (months|years)
22296 * @cfg {Number} startDate default -Infinity
22297 * @cfg {Number} endDate default Infinity
22298 * @cfg {Boolean} todayHighlight default false
22299 * @cfg {Boolean} todayBtn default false
22300 * @cfg {Boolean} calendarWeeks default false
22301 * @cfg {Object} daysOfWeekDisabled default empty
22302 * @cfg {Boolean} singleMode default false (true | false)
22304 * @cfg {Boolean} keyboardNavigation default true
22305 * @cfg {String} language default en
22308 * Create a new DateField
22309 * @param {Object} config The config object
22312 Roo.bootstrap.DateField = function(config){
22313 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22317 * Fires when this field show.
22318 * @param {Roo.bootstrap.DateField} this
22319 * @param {Mixed} date The date value
22324 * Fires when this field hide.
22325 * @param {Roo.bootstrap.DateField} this
22326 * @param {Mixed} date The date value
22331 * Fires when select a date.
22332 * @param {Roo.bootstrap.DateField} this
22333 * @param {Mixed} date The date value
22337 * @event beforeselect
22338 * Fires when before select a date.
22339 * @param {Roo.bootstrap.DateField} this
22340 * @param {Mixed} date The date value
22342 beforeselect : true
22346 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22349 * @cfg {String} format
22350 * The default date format string which can be overriden for localization support. The format must be
22351 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22355 * @cfg {String} altFormats
22356 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22357 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22359 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22367 todayHighlight : false,
22373 keyboardNavigation: true,
22375 calendarWeeks: false,
22377 startDate: -Infinity,
22381 daysOfWeekDisabled: [],
22385 singleMode : false,
22387 UTCDate: function()
22389 return new Date(Date.UTC.apply(Date, arguments));
22392 UTCToday: function()
22394 var today = new Date();
22395 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22398 getDate: function() {
22399 var d = this.getUTCDate();
22400 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22403 getUTCDate: function() {
22407 setDate: function(d) {
22408 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22411 setUTCDate: function(d) {
22413 this.setValue(this.formatDate(this.date));
22416 onRender: function(ct, position)
22419 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22421 this.language = this.language || 'en';
22422 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22423 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22425 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22426 this.format = this.format || 'm/d/y';
22427 this.isInline = false;
22428 this.isInput = true;
22429 this.component = this.el.select('.add-on', true).first() || false;
22430 this.component = (this.component && this.component.length === 0) ? false : this.component;
22431 this.hasInput = this.component && this.inputEl().length;
22433 if (typeof(this.minViewMode === 'string')) {
22434 switch (this.minViewMode) {
22436 this.minViewMode = 1;
22439 this.minViewMode = 2;
22442 this.minViewMode = 0;
22447 if (typeof(this.viewMode === 'string')) {
22448 switch (this.viewMode) {
22461 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22463 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22465 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22467 this.picker().on('mousedown', this.onMousedown, this);
22468 this.picker().on('click', this.onClick, this);
22470 this.picker().addClass('datepicker-dropdown');
22472 this.startViewMode = this.viewMode;
22474 if(this.singleMode){
22475 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22476 v.setVisibilityMode(Roo.Element.DISPLAY);
22480 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22481 v.setStyle('width', '189px');
22485 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22486 if(!this.calendarWeeks){
22491 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22492 v.attr('colspan', function(i, val){
22493 return parseInt(val) + 1;
22498 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22500 this.setStartDate(this.startDate);
22501 this.setEndDate(this.endDate);
22503 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22510 if(this.isInline) {
22515 picker : function()
22517 return this.pickerEl;
22518 // return this.el.select('.datepicker', true).first();
22521 fillDow: function()
22523 var dowCnt = this.weekStart;
22532 if(this.calendarWeeks){
22540 while (dowCnt < this.weekStart + 7) {
22544 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22548 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22551 fillMonths: function()
22554 var months = this.picker().select('>.datepicker-months td', true).first();
22556 months.dom.innerHTML = '';
22562 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22565 months.createChild(month);
22572 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;
22574 if (this.date < this.startDate) {
22575 this.viewDate = new Date(this.startDate);
22576 } else if (this.date > this.endDate) {
22577 this.viewDate = new Date(this.endDate);
22579 this.viewDate = new Date(this.date);
22587 var d = new Date(this.viewDate),
22588 year = d.getUTCFullYear(),
22589 month = d.getUTCMonth(),
22590 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22591 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22592 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22593 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22594 currentDate = this.date && this.date.valueOf(),
22595 today = this.UTCToday();
22597 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22599 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22601 // this.picker.select('>tfoot th.today').
22602 // .text(dates[this.language].today)
22603 // .toggle(this.todayBtn !== false);
22605 this.updateNavArrows();
22608 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22610 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22612 prevMonth.setUTCDate(day);
22614 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22616 var nextMonth = new Date(prevMonth);
22618 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22620 nextMonth = nextMonth.valueOf();
22622 var fillMonths = false;
22624 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22626 while(prevMonth.valueOf() <= nextMonth) {
22629 if (prevMonth.getUTCDay() === this.weekStart) {
22631 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22639 if(this.calendarWeeks){
22640 // ISO 8601: First week contains first thursday.
22641 // ISO also states week starts on Monday, but we can be more abstract here.
22643 // Start of current week: based on weekstart/current date
22644 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22645 // Thursday of this week
22646 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22647 // First Thursday of year, year from thursday
22648 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22649 // Calendar week: ms between thursdays, div ms per day, div 7 days
22650 calWeek = (th - yth) / 864e5 / 7 + 1;
22652 fillMonths.cn.push({
22660 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22662 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22665 if (this.todayHighlight &&
22666 prevMonth.getUTCFullYear() == today.getFullYear() &&
22667 prevMonth.getUTCMonth() == today.getMonth() &&
22668 prevMonth.getUTCDate() == today.getDate()) {
22669 clsName += ' today';
22672 if (currentDate && prevMonth.valueOf() === currentDate) {
22673 clsName += ' active';
22676 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22677 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22678 clsName += ' disabled';
22681 fillMonths.cn.push({
22683 cls: 'day ' + clsName,
22684 html: prevMonth.getDate()
22687 prevMonth.setDate(prevMonth.getDate()+1);
22690 var currentYear = this.date && this.date.getUTCFullYear();
22691 var currentMonth = this.date && this.date.getUTCMonth();
22693 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22695 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22696 v.removeClass('active');
22698 if(currentYear === year && k === currentMonth){
22699 v.addClass('active');
22702 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22703 v.addClass('disabled');
22709 year = parseInt(year/10, 10) * 10;
22711 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22713 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22716 for (var i = -1; i < 11; i++) {
22717 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22719 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22727 showMode: function(dir)
22730 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22733 Roo.each(this.picker().select('>div',true).elements, function(v){
22734 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22737 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22742 if(this.isInline) {
22746 this.picker().removeClass(['bottom', 'top']);
22748 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22750 * place to the top of element!
22754 this.picker().addClass('top');
22755 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22760 this.picker().addClass('bottom');
22762 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22765 parseDate : function(value)
22767 if(!value || value instanceof Date){
22770 var v = Date.parseDate(value, this.format);
22771 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22772 v = Date.parseDate(value, 'Y-m-d');
22774 if(!v && this.altFormats){
22775 if(!this.altFormatsArray){
22776 this.altFormatsArray = this.altFormats.split("|");
22778 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22779 v = Date.parseDate(value, this.altFormatsArray[i]);
22785 formatDate : function(date, fmt)
22787 return (!date || !(date instanceof Date)) ?
22788 date : date.dateFormat(fmt || this.format);
22791 onFocus : function()
22793 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22797 onBlur : function()
22799 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22801 var d = this.inputEl().getValue();
22808 showPopup : function()
22810 this.picker().show();
22814 this.fireEvent('showpopup', this, this.date);
22817 hidePopup : function()
22819 if(this.isInline) {
22822 this.picker().hide();
22823 this.viewMode = this.startViewMode;
22826 this.fireEvent('hidepopup', this, this.date);
22830 onMousedown: function(e)
22832 e.stopPropagation();
22833 e.preventDefault();
22838 Roo.bootstrap.DateField.superclass.keyup.call(this);
22842 setValue: function(v)
22844 if(this.fireEvent('beforeselect', this, v) !== false){
22845 var d = new Date(this.parseDate(v) ).clearTime();
22847 if(isNaN(d.getTime())){
22848 this.date = this.viewDate = '';
22849 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22853 v = this.formatDate(d);
22855 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22857 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22861 this.fireEvent('select', this, this.date);
22865 getValue: function()
22867 return this.formatDate(this.date);
22870 fireKey: function(e)
22872 if (!this.picker().isVisible()){
22873 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22879 var dateChanged = false,
22881 newDate, newViewDate;
22886 e.preventDefault();
22890 if (!this.keyboardNavigation) {
22893 dir = e.keyCode == 37 ? -1 : 1;
22896 newDate = this.moveYear(this.date, dir);
22897 newViewDate = this.moveYear(this.viewDate, dir);
22898 } else if (e.shiftKey){
22899 newDate = this.moveMonth(this.date, dir);
22900 newViewDate = this.moveMonth(this.viewDate, dir);
22902 newDate = new Date(this.date);
22903 newDate.setUTCDate(this.date.getUTCDate() + dir);
22904 newViewDate = new Date(this.viewDate);
22905 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22907 if (this.dateWithinRange(newDate)){
22908 this.date = newDate;
22909 this.viewDate = newViewDate;
22910 this.setValue(this.formatDate(this.date));
22912 e.preventDefault();
22913 dateChanged = true;
22918 if (!this.keyboardNavigation) {
22921 dir = e.keyCode == 38 ? -1 : 1;
22923 newDate = this.moveYear(this.date, dir);
22924 newViewDate = this.moveYear(this.viewDate, dir);
22925 } else if (e.shiftKey){
22926 newDate = this.moveMonth(this.date, dir);
22927 newViewDate = this.moveMonth(this.viewDate, dir);
22929 newDate = new Date(this.date);
22930 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22931 newViewDate = new Date(this.viewDate);
22932 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22934 if (this.dateWithinRange(newDate)){
22935 this.date = newDate;
22936 this.viewDate = newViewDate;
22937 this.setValue(this.formatDate(this.date));
22939 e.preventDefault();
22940 dateChanged = true;
22944 this.setValue(this.formatDate(this.date));
22946 e.preventDefault();
22949 this.setValue(this.formatDate(this.date));
22963 onClick: function(e)
22965 e.stopPropagation();
22966 e.preventDefault();
22968 var target = e.getTarget();
22970 if(target.nodeName.toLowerCase() === 'i'){
22971 target = Roo.get(target).dom.parentNode;
22974 var nodeName = target.nodeName;
22975 var className = target.className;
22976 var html = target.innerHTML;
22977 //Roo.log(nodeName);
22979 switch(nodeName.toLowerCase()) {
22981 switch(className) {
22987 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22988 switch(this.viewMode){
22990 this.viewDate = this.moveMonth(this.viewDate, dir);
22994 this.viewDate = this.moveYear(this.viewDate, dir);
23000 var date = new Date();
23001 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23003 this.setValue(this.formatDate(this.date));
23010 if (className.indexOf('disabled') < 0) {
23011 if (!this.viewDate) {
23012 this.viewDate = new Date();
23014 this.viewDate.setUTCDate(1);
23015 if (className.indexOf('month') > -1) {
23016 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23018 var year = parseInt(html, 10) || 0;
23019 this.viewDate.setUTCFullYear(year);
23023 if(this.singleMode){
23024 this.setValue(this.formatDate(this.viewDate));
23035 //Roo.log(className);
23036 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23037 var day = parseInt(html, 10) || 1;
23038 var year = (this.viewDate || new Date()).getUTCFullYear(),
23039 month = (this.viewDate || new Date()).getUTCMonth();
23041 if (className.indexOf('old') > -1) {
23048 } else if (className.indexOf('new') > -1) {
23056 //Roo.log([year,month,day]);
23057 this.date = this.UTCDate(year, month, day,0,0,0,0);
23058 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23060 //Roo.log(this.formatDate(this.date));
23061 this.setValue(this.formatDate(this.date));
23068 setStartDate: function(startDate)
23070 this.startDate = startDate || -Infinity;
23071 if (this.startDate !== -Infinity) {
23072 this.startDate = this.parseDate(this.startDate);
23075 this.updateNavArrows();
23078 setEndDate: function(endDate)
23080 this.endDate = endDate || Infinity;
23081 if (this.endDate !== Infinity) {
23082 this.endDate = this.parseDate(this.endDate);
23085 this.updateNavArrows();
23088 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23090 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23091 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23092 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23094 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23095 return parseInt(d, 10);
23098 this.updateNavArrows();
23101 updateNavArrows: function()
23103 if(this.singleMode){
23107 var d = new Date(this.viewDate),
23108 year = d.getUTCFullYear(),
23109 month = d.getUTCMonth();
23111 Roo.each(this.picker().select('.prev', true).elements, function(v){
23113 switch (this.viewMode) {
23116 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23122 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23129 Roo.each(this.picker().select('.next', true).elements, function(v){
23131 switch (this.viewMode) {
23134 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23140 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23148 moveMonth: function(date, dir)
23153 var new_date = new Date(date.valueOf()),
23154 day = new_date.getUTCDate(),
23155 month = new_date.getUTCMonth(),
23156 mag = Math.abs(dir),
23158 dir = dir > 0 ? 1 : -1;
23161 // If going back one month, make sure month is not current month
23162 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23164 return new_date.getUTCMonth() == month;
23166 // If going forward one month, make sure month is as expected
23167 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23169 return new_date.getUTCMonth() != new_month;
23171 new_month = month + dir;
23172 new_date.setUTCMonth(new_month);
23173 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23174 if (new_month < 0 || new_month > 11) {
23175 new_month = (new_month + 12) % 12;
23178 // For magnitudes >1, move one month at a time...
23179 for (var i=0; i<mag; i++) {
23180 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23181 new_date = this.moveMonth(new_date, dir);
23183 // ...then reset the day, keeping it in the new month
23184 new_month = new_date.getUTCMonth();
23185 new_date.setUTCDate(day);
23187 return new_month != new_date.getUTCMonth();
23190 // Common date-resetting loop -- if date is beyond end of month, make it
23193 new_date.setUTCDate(--day);
23194 new_date.setUTCMonth(new_month);
23199 moveYear: function(date, dir)
23201 return this.moveMonth(date, dir*12);
23204 dateWithinRange: function(date)
23206 return date >= this.startDate && date <= this.endDate;
23212 this.picker().remove();
23215 validateValue : function(value)
23217 if(this.getVisibilityEl().hasClass('hidden')){
23221 if(value.length < 1) {
23222 if(this.allowBlank){
23228 if(value.length < this.minLength){
23231 if(value.length > this.maxLength){
23235 var vt = Roo.form.VTypes;
23236 if(!vt[this.vtype](value, this)){
23240 if(typeof this.validator == "function"){
23241 var msg = this.validator(value);
23247 if(this.regex && !this.regex.test(value)){
23251 if(typeof(this.parseDate(value)) == 'undefined'){
23255 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23259 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23269 this.date = this.viewDate = '';
23271 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23276 Roo.apply(Roo.bootstrap.DateField, {
23287 html: '<i class="fa fa-arrow-left"/>'
23297 html: '<i class="fa fa-arrow-right"/>'
23339 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23340 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23341 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23342 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23343 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23356 navFnc: 'FullYear',
23361 navFnc: 'FullYear',
23366 Roo.apply(Roo.bootstrap.DateField, {
23370 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23374 cls: 'datepicker-days',
23378 cls: 'table-condensed',
23380 Roo.bootstrap.DateField.head,
23384 Roo.bootstrap.DateField.footer
23391 cls: 'datepicker-months',
23395 cls: 'table-condensed',
23397 Roo.bootstrap.DateField.head,
23398 Roo.bootstrap.DateField.content,
23399 Roo.bootstrap.DateField.footer
23406 cls: 'datepicker-years',
23410 cls: 'table-condensed',
23412 Roo.bootstrap.DateField.head,
23413 Roo.bootstrap.DateField.content,
23414 Roo.bootstrap.DateField.footer
23433 * @class Roo.bootstrap.TimeField
23434 * @extends Roo.bootstrap.Input
23435 * Bootstrap DateField class
23439 * Create a new TimeField
23440 * @param {Object} config The config object
23443 Roo.bootstrap.TimeField = function(config){
23444 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23448 * Fires when this field show.
23449 * @param {Roo.bootstrap.DateField} thisthis
23450 * @param {Mixed} date The date value
23455 * Fires when this field hide.
23456 * @param {Roo.bootstrap.DateField} this
23457 * @param {Mixed} date The date value
23462 * Fires when select a date.
23463 * @param {Roo.bootstrap.DateField} this
23464 * @param {Mixed} date The date value
23470 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23473 * @cfg {String} format
23474 * The default time format string which can be overriden for localization support. The format must be
23475 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23479 getAutoCreate : function()
23481 this.after = '<i class="fa far fa-clock"></i>';
23482 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23486 onRender: function(ct, position)
23489 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23491 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23493 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23495 this.pop = this.picker().select('>.datepicker-time',true).first();
23496 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23498 this.picker().on('mousedown', this.onMousedown, this);
23499 this.picker().on('click', this.onClick, this);
23501 this.picker().addClass('datepicker-dropdown');
23506 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23507 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23508 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23509 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23510 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23511 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23515 fireKey: function(e){
23516 if (!this.picker().isVisible()){
23517 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23523 e.preventDefault();
23531 this.onTogglePeriod();
23534 this.onIncrementMinutes();
23537 this.onDecrementMinutes();
23546 onClick: function(e) {
23547 e.stopPropagation();
23548 e.preventDefault();
23551 picker : function()
23553 return this.pickerEl;
23556 fillTime: function()
23558 var time = this.pop.select('tbody', true).first();
23560 time.dom.innerHTML = '';
23575 cls: 'hours-up fa fas fa-chevron-up'
23595 cls: 'minutes-up fa fas fa-chevron-up'
23616 cls: 'timepicker-hour',
23631 cls: 'timepicker-minute',
23646 cls: 'btn btn-primary period',
23668 cls: 'hours-down fa fas fa-chevron-down'
23688 cls: 'minutes-down fa fas fa-chevron-down'
23706 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23713 var hours = this.time.getHours();
23714 var minutes = this.time.getMinutes();
23727 hours = hours - 12;
23731 hours = '0' + hours;
23735 minutes = '0' + minutes;
23738 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23739 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23740 this.pop.select('button', true).first().dom.innerHTML = period;
23746 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23748 var cls = ['bottom'];
23750 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23757 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23761 //this.picker().setXY(20000,20000);
23762 this.picker().addClass(cls.join('-'));
23766 Roo.each(cls, function(c){
23771 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23772 //_this.picker().setTop(_this.inputEl().getHeight());
23776 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23778 //_this.picker().setTop(0 - _this.picker().getHeight());
23783 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23787 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23795 onFocus : function()
23797 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23801 onBlur : function()
23803 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23809 this.picker().show();
23814 this.fireEvent('show', this, this.date);
23819 this.picker().hide();
23822 this.fireEvent('hide', this, this.date);
23825 setTime : function()
23828 this.setValue(this.time.format(this.format));
23830 this.fireEvent('select', this, this.date);
23835 onMousedown: function(e){
23836 e.stopPropagation();
23837 e.preventDefault();
23840 onIncrementHours: function()
23842 Roo.log('onIncrementHours');
23843 this.time = this.time.add(Date.HOUR, 1);
23848 onDecrementHours: function()
23850 Roo.log('onDecrementHours');
23851 this.time = this.time.add(Date.HOUR, -1);
23855 onIncrementMinutes: function()
23857 Roo.log('onIncrementMinutes');
23858 this.time = this.time.add(Date.MINUTE, 1);
23862 onDecrementMinutes: function()
23864 Roo.log('onDecrementMinutes');
23865 this.time = this.time.add(Date.MINUTE, -1);
23869 onTogglePeriod: function()
23871 Roo.log('onTogglePeriod');
23872 this.time = this.time.add(Date.HOUR, 12);
23880 Roo.apply(Roo.bootstrap.TimeField, {
23884 cls: 'datepicker dropdown-menu',
23888 cls: 'datepicker-time',
23892 cls: 'table-condensed',
23921 cls: 'btn btn-info ok',
23949 * @class Roo.bootstrap.MonthField
23950 * @extends Roo.bootstrap.Input
23951 * Bootstrap MonthField class
23953 * @cfg {String} language default en
23956 * Create a new MonthField
23957 * @param {Object} config The config object
23960 Roo.bootstrap.MonthField = function(config){
23961 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23966 * Fires when this field show.
23967 * @param {Roo.bootstrap.MonthField} this
23968 * @param {Mixed} date The date value
23973 * Fires when this field hide.
23974 * @param {Roo.bootstrap.MonthField} this
23975 * @param {Mixed} date The date value
23980 * Fires when select a date.
23981 * @param {Roo.bootstrap.MonthField} this
23982 * @param {String} oldvalue The old value
23983 * @param {String} newvalue The new value
23989 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23991 onRender: function(ct, position)
23994 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23996 this.language = this.language || 'en';
23997 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23998 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24000 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24001 this.isInline = false;
24002 this.isInput = true;
24003 this.component = this.el.select('.add-on', true).first() || false;
24004 this.component = (this.component && this.component.length === 0) ? false : this.component;
24005 this.hasInput = this.component && this.inputEL().length;
24007 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24009 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24011 this.picker().on('mousedown', this.onMousedown, this);
24012 this.picker().on('click', this.onClick, this);
24014 this.picker().addClass('datepicker-dropdown');
24016 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24017 v.setStyle('width', '189px');
24024 if(this.isInline) {
24030 setValue: function(v, suppressEvent)
24032 var o = this.getValue();
24034 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24038 if(suppressEvent !== true){
24039 this.fireEvent('select', this, o, v);
24044 getValue: function()
24049 onClick: function(e)
24051 e.stopPropagation();
24052 e.preventDefault();
24054 var target = e.getTarget();
24056 if(target.nodeName.toLowerCase() === 'i'){
24057 target = Roo.get(target).dom.parentNode;
24060 var nodeName = target.nodeName;
24061 var className = target.className;
24062 var html = target.innerHTML;
24064 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24068 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24070 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24076 picker : function()
24078 return this.pickerEl;
24081 fillMonths: function()
24084 var months = this.picker().select('>.datepicker-months td', true).first();
24086 months.dom.innerHTML = '';
24092 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24095 months.createChild(month);
24104 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24105 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24108 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24109 e.removeClass('active');
24111 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24112 e.addClass('active');
24119 if(this.isInline) {
24123 this.picker().removeClass(['bottom', 'top']);
24125 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24127 * place to the top of element!
24131 this.picker().addClass('top');
24132 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24137 this.picker().addClass('bottom');
24139 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24142 onFocus : function()
24144 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24148 onBlur : function()
24150 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24152 var d = this.inputEl().getValue();
24161 this.picker().show();
24162 this.picker().select('>.datepicker-months', true).first().show();
24166 this.fireEvent('show', this, this.date);
24171 if(this.isInline) {
24174 this.picker().hide();
24175 this.fireEvent('hide', this, this.date);
24179 onMousedown: function(e)
24181 e.stopPropagation();
24182 e.preventDefault();
24187 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24191 fireKey: function(e)
24193 if (!this.picker().isVisible()){
24194 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24205 e.preventDefault();
24209 dir = e.keyCode == 37 ? -1 : 1;
24211 this.vIndex = this.vIndex + dir;
24213 if(this.vIndex < 0){
24217 if(this.vIndex > 11){
24221 if(isNaN(this.vIndex)){
24225 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24231 dir = e.keyCode == 38 ? -1 : 1;
24233 this.vIndex = this.vIndex + dir * 4;
24235 if(this.vIndex < 0){
24239 if(this.vIndex > 11){
24243 if(isNaN(this.vIndex)){
24247 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24252 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24253 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24257 e.preventDefault();
24260 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24261 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24277 this.picker().remove();
24282 Roo.apply(Roo.bootstrap.MonthField, {
24301 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24302 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24307 Roo.apply(Roo.bootstrap.MonthField, {
24311 cls: 'datepicker dropdown-menu roo-dynamic',
24315 cls: 'datepicker-months',
24319 cls: 'table-condensed',
24321 Roo.bootstrap.DateField.content
24341 * @class Roo.bootstrap.CheckBox
24342 * @extends Roo.bootstrap.Input
24343 * Bootstrap CheckBox class
24345 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24346 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24347 * @cfg {String} boxLabel The text that appears beside the checkbox
24348 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24349 * @cfg {Boolean} checked initnal the element
24350 * @cfg {Boolean} inline inline the element (default false)
24351 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24352 * @cfg {String} tooltip label tooltip
24355 * Create a new CheckBox
24356 * @param {Object} config The config object
24359 Roo.bootstrap.CheckBox = function(config){
24360 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24365 * Fires when the element is checked or unchecked.
24366 * @param {Roo.bootstrap.CheckBox} this This input
24367 * @param {Boolean} checked The new checked value
24372 * Fires when the element is click.
24373 * @param {Roo.bootstrap.CheckBox} this This input
24380 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24382 inputType: 'checkbox',
24391 // checkbox success does not make any sense really..
24396 getAutoCreate : function()
24398 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24404 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24407 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24413 type : this.inputType,
24414 value : this.inputValue,
24415 cls : 'roo-' + this.inputType, //'form-box',
24416 placeholder : this.placeholder || ''
24420 if(this.inputType != 'radio'){
24424 cls : 'roo-hidden-value',
24425 value : this.checked ? this.inputValue : this.valueOff
24430 if (this.weight) { // Validity check?
24431 cfg.cls += " " + this.inputType + "-" + this.weight;
24434 if (this.disabled) {
24435 input.disabled=true;
24439 input.checked = this.checked;
24444 input.name = this.name;
24446 if(this.inputType != 'radio'){
24447 hidden.name = this.name;
24448 input.name = '_hidden_' + this.name;
24453 input.cls += ' input-' + this.size;
24458 ['xs','sm','md','lg'].map(function(size){
24459 if (settings[size]) {
24460 cfg.cls += ' col-' + size + '-' + settings[size];
24464 var inputblock = input;
24466 if (this.before || this.after) {
24469 cls : 'input-group',
24474 inputblock.cn.push({
24476 cls : 'input-group-addon',
24481 inputblock.cn.push(input);
24483 if(this.inputType != 'radio'){
24484 inputblock.cn.push(hidden);
24488 inputblock.cn.push({
24490 cls : 'input-group-addon',
24496 var boxLabelCfg = false;
24502 //'for': id, // box label is handled by onclick - so no for...
24504 html: this.boxLabel
24507 boxLabelCfg.tooltip = this.tooltip;
24513 if (align ==='left' && this.fieldLabel.length) {
24514 // Roo.log("left and has label");
24519 cls : 'control-label',
24520 html : this.fieldLabel
24531 cfg.cn[1].cn.push(boxLabelCfg);
24534 if(this.labelWidth > 12){
24535 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24538 if(this.labelWidth < 13 && this.labelmd == 0){
24539 this.labelmd = this.labelWidth;
24542 if(this.labellg > 0){
24543 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24544 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24547 if(this.labelmd > 0){
24548 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24549 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24552 if(this.labelsm > 0){
24553 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24554 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24557 if(this.labelxs > 0){
24558 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24559 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24562 } else if ( this.fieldLabel.length) {
24563 // Roo.log(" label");
24567 tag: this.boxLabel ? 'span' : 'label',
24569 cls: 'control-label box-input-label',
24570 //cls : 'input-group-addon',
24571 html : this.fieldLabel
24578 cfg.cn.push(boxLabelCfg);
24583 // Roo.log(" no label && no align");
24584 cfg.cn = [ inputblock ] ;
24586 cfg.cn.push(boxLabelCfg);
24594 if(this.inputType != 'radio'){
24595 cfg.cn.push(hidden);
24603 * return the real input element.
24605 inputEl: function ()
24607 return this.el.select('input.roo-' + this.inputType,true).first();
24609 hiddenEl: function ()
24611 return this.el.select('input.roo-hidden-value',true).first();
24614 labelEl: function()
24616 return this.el.select('label.control-label',true).first();
24618 /* depricated... */
24622 return this.labelEl();
24625 boxLabelEl: function()
24627 return this.el.select('label.box-label',true).first();
24630 initEvents : function()
24632 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24634 this.inputEl().on('click', this.onClick, this);
24636 if (this.boxLabel) {
24637 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24640 this.startValue = this.getValue();
24643 Roo.bootstrap.CheckBox.register(this);
24647 onClick : function(e)
24649 if(this.fireEvent('click', this, e) !== false){
24650 this.setChecked(!this.checked);
24655 setChecked : function(state,suppressEvent)
24657 this.startValue = this.getValue();
24659 if(this.inputType == 'radio'){
24661 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24662 e.dom.checked = false;
24665 this.inputEl().dom.checked = true;
24667 this.inputEl().dom.value = this.inputValue;
24669 if(suppressEvent !== true){
24670 this.fireEvent('check', this, true);
24678 this.checked = state;
24680 this.inputEl().dom.checked = state;
24683 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24685 if(suppressEvent !== true){
24686 this.fireEvent('check', this, state);
24692 getValue : function()
24694 if(this.inputType == 'radio'){
24695 return this.getGroupValue();
24698 return this.hiddenEl().dom.value;
24702 getGroupValue : function()
24704 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24708 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24711 setValue : function(v,suppressEvent)
24713 if(this.inputType == 'radio'){
24714 this.setGroupValue(v, suppressEvent);
24718 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24723 setGroupValue : function(v, suppressEvent)
24725 this.startValue = this.getValue();
24727 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24728 e.dom.checked = false;
24730 if(e.dom.value == v){
24731 e.dom.checked = true;
24735 if(suppressEvent !== true){
24736 this.fireEvent('check', this, true);
24744 validate : function()
24746 if(this.getVisibilityEl().hasClass('hidden')){
24752 (this.inputType == 'radio' && this.validateRadio()) ||
24753 (this.inputType == 'checkbox' && this.validateCheckbox())
24759 this.markInvalid();
24763 validateRadio : function()
24765 if(this.getVisibilityEl().hasClass('hidden')){
24769 if(this.allowBlank){
24775 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24776 if(!e.dom.checked){
24788 validateCheckbox : function()
24791 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24792 //return (this.getValue() == this.inputValue) ? true : false;
24795 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24803 for(var i in group){
24804 if(group[i].el.isVisible(true)){
24812 for(var i in group){
24817 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24824 * Mark this field as valid
24826 markValid : function()
24830 this.fireEvent('valid', this);
24832 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24835 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24842 if(this.inputType == 'radio'){
24843 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24844 var fg = e.findParent('.form-group', false, true);
24845 if (Roo.bootstrap.version == 3) {
24846 fg.removeClass([_this.invalidClass, _this.validClass]);
24847 fg.addClass(_this.validClass);
24849 fg.removeClass(['is-valid', 'is-invalid']);
24850 fg.addClass('is-valid');
24858 var fg = this.el.findParent('.form-group', false, true);
24859 if (Roo.bootstrap.version == 3) {
24860 fg.removeClass([this.invalidClass, this.validClass]);
24861 fg.addClass(this.validClass);
24863 fg.removeClass(['is-valid', 'is-invalid']);
24864 fg.addClass('is-valid');
24869 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24875 for(var i in group){
24876 var fg = group[i].el.findParent('.form-group', false, true);
24877 if (Roo.bootstrap.version == 3) {
24878 fg.removeClass([this.invalidClass, this.validClass]);
24879 fg.addClass(this.validClass);
24881 fg.removeClass(['is-valid', 'is-invalid']);
24882 fg.addClass('is-valid');
24888 * Mark this field as invalid
24889 * @param {String} msg The validation message
24891 markInvalid : function(msg)
24893 if(this.allowBlank){
24899 this.fireEvent('invalid', this, msg);
24901 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24904 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24908 label.markInvalid();
24911 if(this.inputType == 'radio'){
24913 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24914 var fg = e.findParent('.form-group', false, true);
24915 if (Roo.bootstrap.version == 3) {
24916 fg.removeClass([_this.invalidClass, _this.validClass]);
24917 fg.addClass(_this.invalidClass);
24919 fg.removeClass(['is-invalid', 'is-valid']);
24920 fg.addClass('is-invalid');
24928 var fg = this.el.findParent('.form-group', false, true);
24929 if (Roo.bootstrap.version == 3) {
24930 fg.removeClass([_this.invalidClass, _this.validClass]);
24931 fg.addClass(_this.invalidClass);
24933 fg.removeClass(['is-invalid', 'is-valid']);
24934 fg.addClass('is-invalid');
24939 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24945 for(var i in group){
24946 var fg = group[i].el.findParent('.form-group', false, true);
24947 if (Roo.bootstrap.version == 3) {
24948 fg.removeClass([_this.invalidClass, _this.validClass]);
24949 fg.addClass(_this.invalidClass);
24951 fg.removeClass(['is-invalid', 'is-valid']);
24952 fg.addClass('is-invalid');
24958 clearInvalid : function()
24960 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24962 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24964 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24966 if (label && label.iconEl) {
24967 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24968 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24972 disable : function()
24974 if(this.inputType != 'radio'){
24975 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24982 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24983 _this.getActionEl().addClass(this.disabledClass);
24984 e.dom.disabled = true;
24988 this.disabled = true;
24989 this.fireEvent("disable", this);
24993 enable : function()
24995 if(this.inputType != 'radio'){
24996 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25003 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25004 _this.getActionEl().removeClass(this.disabledClass);
25005 e.dom.disabled = false;
25009 this.disabled = false;
25010 this.fireEvent("enable", this);
25014 setBoxLabel : function(v)
25019 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25025 Roo.apply(Roo.bootstrap.CheckBox, {
25030 * register a CheckBox Group
25031 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25033 register : function(checkbox)
25035 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25036 this.groups[checkbox.groupId] = {};
25039 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25043 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25047 * fetch a CheckBox Group based on the group ID
25048 * @param {string} the group ID
25049 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25051 get: function(groupId) {
25052 if (typeof(this.groups[groupId]) == 'undefined') {
25056 return this.groups[groupId] ;
25069 * @class Roo.bootstrap.Radio
25070 * @extends Roo.bootstrap.Component
25071 * Bootstrap Radio class
25072 * @cfg {String} boxLabel - the label associated
25073 * @cfg {String} value - the value of radio
25076 * Create a new Radio
25077 * @param {Object} config The config object
25079 Roo.bootstrap.Radio = function(config){
25080 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25084 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25090 getAutoCreate : function()
25094 cls : 'form-group radio',
25099 html : this.boxLabel
25107 initEvents : function()
25109 this.parent().register(this);
25111 this.el.on('click', this.onClick, this);
25115 onClick : function(e)
25117 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25118 this.setChecked(true);
25122 setChecked : function(state, suppressEvent)
25124 this.parent().setValue(this.value, suppressEvent);
25128 setBoxLabel : function(v)
25133 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25148 * @class Roo.bootstrap.SecurePass
25149 * @extends Roo.bootstrap.Input
25150 * Bootstrap SecurePass class
25154 * Create a new SecurePass
25155 * @param {Object} config The config object
25158 Roo.bootstrap.SecurePass = function (config) {
25159 // these go here, so the translation tool can replace them..
25161 PwdEmpty: "Please type a password, and then retype it to confirm.",
25162 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25163 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25164 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25165 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25166 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25167 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25168 TooWeak: "Your password is Too Weak."
25170 this.meterLabel = "Password strength:";
25171 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25172 this.meterClass = [
25173 "roo-password-meter-tooweak",
25174 "roo-password-meter-weak",
25175 "roo-password-meter-medium",
25176 "roo-password-meter-strong",
25177 "roo-password-meter-grey"
25182 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25185 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25187 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25189 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25190 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25191 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25192 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25193 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25194 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25195 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25205 * @cfg {String/Object} Label for the strength meter (defaults to
25206 * 'Password strength:')
25211 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25212 * ['Weak', 'Medium', 'Strong'])
25215 pwdStrengths: false,
25228 initEvents: function ()
25230 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25232 if (this.el.is('input[type=password]') && Roo.isSafari) {
25233 this.el.on('keydown', this.SafariOnKeyDown, this);
25236 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25239 onRender: function (ct, position)
25241 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25242 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25243 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25245 this.trigger.createChild({
25250 cls: 'roo-password-meter-grey col-xs-12',
25253 //width: this.meterWidth + 'px'
25257 cls: 'roo-password-meter-text'
25263 if (this.hideTrigger) {
25264 this.trigger.setDisplayed(false);
25266 this.setSize(this.width || '', this.height || '');
25269 onDestroy: function ()
25271 if (this.trigger) {
25272 this.trigger.removeAllListeners();
25273 this.trigger.remove();
25276 this.wrap.remove();
25278 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25281 checkStrength: function ()
25283 var pwd = this.inputEl().getValue();
25284 if (pwd == this._lastPwd) {
25289 if (this.ClientSideStrongPassword(pwd)) {
25291 } else if (this.ClientSideMediumPassword(pwd)) {
25293 } else if (this.ClientSideWeakPassword(pwd)) {
25299 Roo.log('strength1: ' + strength);
25301 //var pm = this.trigger.child('div/div/div').dom;
25302 var pm = this.trigger.child('div/div');
25303 pm.removeClass(this.meterClass);
25304 pm.addClass(this.meterClass[strength]);
25307 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25309 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25311 this._lastPwd = pwd;
25315 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25317 this._lastPwd = '';
25319 var pm = this.trigger.child('div/div');
25320 pm.removeClass(this.meterClass);
25321 pm.addClass('roo-password-meter-grey');
25324 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25327 this.inputEl().dom.type='password';
25330 validateValue: function (value)
25332 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25335 if (value.length == 0) {
25336 if (this.allowBlank) {
25337 this.clearInvalid();
25341 this.markInvalid(this.errors.PwdEmpty);
25342 this.errorMsg = this.errors.PwdEmpty;
25350 if (!value.match(/[\x21-\x7e]+/)) {
25351 this.markInvalid(this.errors.PwdBadChar);
25352 this.errorMsg = this.errors.PwdBadChar;
25355 if (value.length < 6) {
25356 this.markInvalid(this.errors.PwdShort);
25357 this.errorMsg = this.errors.PwdShort;
25360 if (value.length > 16) {
25361 this.markInvalid(this.errors.PwdLong);
25362 this.errorMsg = this.errors.PwdLong;
25366 if (this.ClientSideStrongPassword(value)) {
25368 } else if (this.ClientSideMediumPassword(value)) {
25370 } else if (this.ClientSideWeakPassword(value)) {
25377 if (strength < 2) {
25378 //this.markInvalid(this.errors.TooWeak);
25379 this.errorMsg = this.errors.TooWeak;
25384 console.log('strength2: ' + strength);
25386 //var pm = this.trigger.child('div/div/div').dom;
25388 var pm = this.trigger.child('div/div');
25389 pm.removeClass(this.meterClass);
25390 pm.addClass(this.meterClass[strength]);
25392 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25394 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25396 this.errorMsg = '';
25400 CharacterSetChecks: function (type)
25403 this.fResult = false;
25406 isctype: function (character, type)
25409 case this.kCapitalLetter:
25410 if (character >= 'A' && character <= 'Z') {
25415 case this.kSmallLetter:
25416 if (character >= 'a' && character <= 'z') {
25422 if (character >= '0' && character <= '9') {
25427 case this.kPunctuation:
25428 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25439 IsLongEnough: function (pwd, size)
25441 return !(pwd == null || isNaN(size) || pwd.length < size);
25444 SpansEnoughCharacterSets: function (word, nb)
25446 if (!this.IsLongEnough(word, nb))
25451 var characterSetChecks = new Array(
25452 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25453 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25456 for (var index = 0; index < word.length; ++index) {
25457 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25458 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25459 characterSetChecks[nCharSet].fResult = true;
25466 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25467 if (characterSetChecks[nCharSet].fResult) {
25472 if (nCharSets < nb) {
25478 ClientSideStrongPassword: function (pwd)
25480 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25483 ClientSideMediumPassword: function (pwd)
25485 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25488 ClientSideWeakPassword: function (pwd)
25490 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25493 })//<script type="text/javascript">
25496 * Based Ext JS Library 1.1.1
25497 * Copyright(c) 2006-2007, Ext JS, LLC.
25503 * @class Roo.HtmlEditorCore
25504 * @extends Roo.Component
25505 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25507 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25510 Roo.HtmlEditorCore = function(config){
25513 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25518 * @event initialize
25519 * Fires when the editor is fully initialized (including the iframe)
25520 * @param {Roo.HtmlEditorCore} this
25525 * Fires when the editor is first receives the focus. Any insertion must wait
25526 * until after this event.
25527 * @param {Roo.HtmlEditorCore} this
25531 * @event beforesync
25532 * Fires before the textarea is updated with content from the editor iframe. Return false
25533 * to cancel the sync.
25534 * @param {Roo.HtmlEditorCore} this
25535 * @param {String} html
25539 * @event beforepush
25540 * Fires before the iframe editor is updated with content from the textarea. Return false
25541 * to cancel the push.
25542 * @param {Roo.HtmlEditorCore} this
25543 * @param {String} html
25548 * Fires when the textarea is updated with content from the editor iframe.
25549 * @param {Roo.HtmlEditorCore} this
25550 * @param {String} html
25555 * Fires when the iframe editor is updated with content from the textarea.
25556 * @param {Roo.HtmlEditorCore} this
25557 * @param {String} html
25562 * @event editorevent
25563 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25564 * @param {Roo.HtmlEditorCore} this
25570 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25572 // defaults : white / black...
25573 this.applyBlacklists();
25580 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25584 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25590 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25595 * @cfg {Number} height (in pixels)
25599 * @cfg {Number} width (in pixels)
25604 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25607 stylesheets: false,
25612 // private properties
25613 validationEvent : false,
25615 initialized : false,
25617 sourceEditMode : false,
25618 onFocus : Roo.emptyFn,
25620 hideMode:'offsets',
25624 // blacklist + whitelisted elements..
25631 * Protected method that will not generally be called directly. It
25632 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25633 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25635 getDocMarkup : function(){
25639 // inherit styels from page...??
25640 if (this.stylesheets === false) {
25642 Roo.get(document.head).select('style').each(function(node) {
25643 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25646 Roo.get(document.head).select('link').each(function(node) {
25647 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25650 } else if (!this.stylesheets.length) {
25652 st = '<style type="text/css">' +
25653 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25656 for (var i in this.stylesheets) {
25657 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25662 st += '<style type="text/css">' +
25663 'IMG { cursor: pointer } ' +
25666 var cls = 'roo-htmleditor-body';
25668 if(this.bodyCls.length){
25669 cls += ' ' + this.bodyCls;
25672 return '<html><head>' + st +
25673 //<style type="text/css">' +
25674 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25676 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25680 onRender : function(ct, position)
25683 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25684 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25687 this.el.dom.style.border = '0 none';
25688 this.el.dom.setAttribute('tabIndex', -1);
25689 this.el.addClass('x-hidden hide');
25693 if(Roo.isIE){ // fix IE 1px bogus margin
25694 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25698 this.frameId = Roo.id();
25702 var iframe = this.owner.wrap.createChild({
25704 cls: 'form-control', // bootstrap..
25706 name: this.frameId,
25707 frameBorder : 'no',
25708 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25713 this.iframe = iframe.dom;
25715 this.assignDocWin();
25717 this.doc.designMode = 'on';
25720 this.doc.write(this.getDocMarkup());
25724 var task = { // must defer to wait for browser to be ready
25726 //console.log("run task?" + this.doc.readyState);
25727 this.assignDocWin();
25728 if(this.doc.body || this.doc.readyState == 'complete'){
25730 this.doc.designMode="on";
25734 Roo.TaskMgr.stop(task);
25735 this.initEditor.defer(10, this);
25742 Roo.TaskMgr.start(task);
25747 onResize : function(w, h)
25749 Roo.log('resize: ' +w + ',' + h );
25750 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25754 if(typeof w == 'number'){
25756 this.iframe.style.width = w + 'px';
25758 if(typeof h == 'number'){
25760 this.iframe.style.height = h + 'px';
25762 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25769 * Toggles the editor between standard and source edit mode.
25770 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25772 toggleSourceEdit : function(sourceEditMode){
25774 this.sourceEditMode = sourceEditMode === true;
25776 if(this.sourceEditMode){
25778 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25781 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25782 //this.iframe.className = '';
25785 //this.setSize(this.owner.wrap.getSize());
25786 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25793 * Protected method that will not generally be called directly. If you need/want
25794 * custom HTML cleanup, this is the method you should override.
25795 * @param {String} html The HTML to be cleaned
25796 * return {String} The cleaned HTML
25798 cleanHtml : function(html){
25799 html = String(html);
25800 if(html.length > 5){
25801 if(Roo.isSafari){ // strip safari nonsense
25802 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25805 if(html == ' '){
25812 * HTML Editor -> Textarea
25813 * Protected method that will not generally be called directly. Syncs the contents
25814 * of the editor iframe with the textarea.
25816 syncValue : function(){
25817 if(this.initialized){
25818 var bd = (this.doc.body || this.doc.documentElement);
25819 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25820 var html = bd.innerHTML;
25822 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25823 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25825 html = '<div style="'+m[0]+'">' + html + '</div>';
25828 html = this.cleanHtml(html);
25829 // fix up the special chars.. normaly like back quotes in word...
25830 // however we do not want to do this with chinese..
25831 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25833 var cc = match.charCodeAt();
25835 // Get the character value, handling surrogate pairs
25836 if (match.length == 2) {
25837 // It's a surrogate pair, calculate the Unicode code point
25838 var high = match.charCodeAt(0) - 0xD800;
25839 var low = match.charCodeAt(1) - 0xDC00;
25840 cc = (high * 0x400) + low + 0x10000;
25842 (cc >= 0x4E00 && cc < 0xA000 ) ||
25843 (cc >= 0x3400 && cc < 0x4E00 ) ||
25844 (cc >= 0xf900 && cc < 0xfb00 )
25849 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25850 return "&#" + cc + ";";
25857 if(this.owner.fireEvent('beforesync', this, html) !== false){
25858 this.el.dom.value = html;
25859 this.owner.fireEvent('sync', this, html);
25865 * Protected method that will not generally be called directly. Pushes the value of the textarea
25866 * into the iframe editor.
25868 pushValue : function(){
25869 if(this.initialized){
25870 var v = this.el.dom.value.trim();
25872 // if(v.length < 1){
25876 if(this.owner.fireEvent('beforepush', this, v) !== false){
25877 var d = (this.doc.body || this.doc.documentElement);
25879 this.cleanUpPaste();
25880 this.el.dom.value = d.innerHTML;
25881 this.owner.fireEvent('push', this, v);
25887 deferFocus : function(){
25888 this.focus.defer(10, this);
25892 focus : function(){
25893 if(this.win && !this.sourceEditMode){
25900 assignDocWin: function()
25902 var iframe = this.iframe;
25905 this.doc = iframe.contentWindow.document;
25906 this.win = iframe.contentWindow;
25908 // if (!Roo.get(this.frameId)) {
25911 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25912 // this.win = Roo.get(this.frameId).dom.contentWindow;
25914 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25918 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25919 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25924 initEditor : function(){
25925 //console.log("INIT EDITOR");
25926 this.assignDocWin();
25930 this.doc.designMode="on";
25932 this.doc.write(this.getDocMarkup());
25935 var dbody = (this.doc.body || this.doc.documentElement);
25936 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25937 // this copies styles from the containing element into thsi one..
25938 // not sure why we need all of this..
25939 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25941 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25942 //ss['background-attachment'] = 'fixed'; // w3c
25943 dbody.bgProperties = 'fixed'; // ie
25944 //Roo.DomHelper.applyStyles(dbody, ss);
25945 Roo.EventManager.on(this.doc, {
25946 //'mousedown': this.onEditorEvent,
25947 'mouseup': this.onEditorEvent,
25948 'dblclick': this.onEditorEvent,
25949 'click': this.onEditorEvent,
25950 'keyup': this.onEditorEvent,
25955 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25957 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25958 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25960 this.initialized = true;
25962 this.owner.fireEvent('initialize', this);
25967 onDestroy : function(){
25973 //for (var i =0; i < this.toolbars.length;i++) {
25974 // // fixme - ask toolbars for heights?
25975 // this.toolbars[i].onDestroy();
25978 //this.wrap.dom.innerHTML = '';
25979 //this.wrap.remove();
25984 onFirstFocus : function(){
25986 this.assignDocWin();
25989 this.activated = true;
25992 if(Roo.isGecko){ // prevent silly gecko errors
25994 var s = this.win.getSelection();
25995 if(!s.focusNode || s.focusNode.nodeType != 3){
25996 var r = s.getRangeAt(0);
25997 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26002 this.execCmd('useCSS', true);
26003 this.execCmd('styleWithCSS', false);
26006 this.owner.fireEvent('activate', this);
26010 adjustFont: function(btn){
26011 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26012 //if(Roo.isSafari){ // safari
26015 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26016 if(Roo.isSafari){ // safari
26017 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26018 v = (v < 10) ? 10 : v;
26019 v = (v > 48) ? 48 : v;
26020 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26025 v = Math.max(1, v+adjust);
26027 this.execCmd('FontSize', v );
26030 onEditorEvent : function(e)
26032 this.owner.fireEvent('editorevent', this, e);
26033 // this.updateToolbar();
26034 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26037 insertTag : function(tg)
26039 // could be a bit smarter... -> wrap the current selected tRoo..
26040 if (tg.toLowerCase() == 'span' ||
26041 tg.toLowerCase() == 'code' ||
26042 tg.toLowerCase() == 'sup' ||
26043 tg.toLowerCase() == 'sub'
26046 range = this.createRange(this.getSelection());
26047 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26048 wrappingNode.appendChild(range.extractContents());
26049 range.insertNode(wrappingNode);
26056 this.execCmd("formatblock", tg);
26060 insertText : function(txt)
26064 var range = this.createRange();
26065 range.deleteContents();
26066 //alert(Sender.getAttribute('label'));
26068 range.insertNode(this.doc.createTextNode(txt));
26074 * Executes a Midas editor command on the editor document and performs necessary focus and
26075 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26076 * @param {String} cmd The Midas command
26077 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26079 relayCmd : function(cmd, value){
26081 this.execCmd(cmd, value);
26082 this.owner.fireEvent('editorevent', this);
26083 //this.updateToolbar();
26084 this.owner.deferFocus();
26088 * Executes a Midas editor command directly on the editor document.
26089 * For visual commands, you should use {@link #relayCmd} instead.
26090 * <b>This should only be called after the editor is initialized.</b>
26091 * @param {String} cmd The Midas command
26092 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26094 execCmd : function(cmd, value){
26095 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26102 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26104 * @param {String} text | dom node..
26106 insertAtCursor : function(text)
26109 if(!this.activated){
26115 var r = this.doc.selection.createRange();
26126 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26130 // from jquery ui (MIT licenced)
26132 var win = this.win;
26134 if (win.getSelection && win.getSelection().getRangeAt) {
26135 range = win.getSelection().getRangeAt(0);
26136 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26137 range.insertNode(node);
26138 } else if (win.document.selection && win.document.selection.createRange) {
26139 // no firefox support
26140 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26141 win.document.selection.createRange().pasteHTML(txt);
26143 // no firefox support
26144 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26145 this.execCmd('InsertHTML', txt);
26154 mozKeyPress : function(e){
26156 var c = e.getCharCode(), cmd;
26159 c = String.fromCharCode(c).toLowerCase();
26173 this.cleanUpPaste.defer(100, this);
26181 e.preventDefault();
26189 fixKeys : function(){ // load time branching for fastest keydown performance
26191 return function(e){
26192 var k = e.getKey(), r;
26195 r = this.doc.selection.createRange();
26198 r.pasteHTML('    ');
26205 r = this.doc.selection.createRange();
26207 var target = r.parentElement();
26208 if(!target || target.tagName.toLowerCase() != 'li'){
26210 r.pasteHTML('<br />');
26216 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26217 this.cleanUpPaste.defer(100, this);
26223 }else if(Roo.isOpera){
26224 return function(e){
26225 var k = e.getKey();
26229 this.execCmd('InsertHTML','    ');
26232 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26233 this.cleanUpPaste.defer(100, this);
26238 }else if(Roo.isSafari){
26239 return function(e){
26240 var k = e.getKey();
26244 this.execCmd('InsertText','\t');
26248 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26249 this.cleanUpPaste.defer(100, this);
26257 getAllAncestors: function()
26259 var p = this.getSelectedNode();
26262 a.push(p); // push blank onto stack..
26263 p = this.getParentElement();
26267 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26271 a.push(this.doc.body);
26275 lastSelNode : false,
26278 getSelection : function()
26280 this.assignDocWin();
26281 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26284 getSelectedNode: function()
26286 // this may only work on Gecko!!!
26288 // should we cache this!!!!
26293 var range = this.createRange(this.getSelection()).cloneRange();
26296 var parent = range.parentElement();
26298 var testRange = range.duplicate();
26299 testRange.moveToElementText(parent);
26300 if (testRange.inRange(range)) {
26303 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26306 parent = parent.parentElement;
26311 // is ancestor a text element.
26312 var ac = range.commonAncestorContainer;
26313 if (ac.nodeType == 3) {
26314 ac = ac.parentNode;
26317 var ar = ac.childNodes;
26320 var other_nodes = [];
26321 var has_other_nodes = false;
26322 for (var i=0;i<ar.length;i++) {
26323 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26326 // fullly contained node.
26328 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26333 // probably selected..
26334 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26335 other_nodes.push(ar[i]);
26339 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26344 has_other_nodes = true;
26346 if (!nodes.length && other_nodes.length) {
26347 nodes= other_nodes;
26349 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26355 createRange: function(sel)
26357 // this has strange effects when using with
26358 // top toolbar - not sure if it's a great idea.
26359 //this.editor.contentWindow.focus();
26360 if (typeof sel != "undefined") {
26362 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26364 return this.doc.createRange();
26367 return this.doc.createRange();
26370 getParentElement: function()
26373 this.assignDocWin();
26374 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26376 var range = this.createRange(sel);
26379 var p = range.commonAncestorContainer;
26380 while (p.nodeType == 3) { // text node
26391 * Range intersection.. the hard stuff...
26395 * [ -- selected range --- ]
26399 * if end is before start or hits it. fail.
26400 * if start is after end or hits it fail.
26402 * if either hits (but other is outside. - then it's not
26408 // @see http://www.thismuchiknow.co.uk/?p=64.
26409 rangeIntersectsNode : function(range, node)
26411 var nodeRange = node.ownerDocument.createRange();
26413 nodeRange.selectNode(node);
26415 nodeRange.selectNodeContents(node);
26418 var rangeStartRange = range.cloneRange();
26419 rangeStartRange.collapse(true);
26421 var rangeEndRange = range.cloneRange();
26422 rangeEndRange.collapse(false);
26424 var nodeStartRange = nodeRange.cloneRange();
26425 nodeStartRange.collapse(true);
26427 var nodeEndRange = nodeRange.cloneRange();
26428 nodeEndRange.collapse(false);
26430 return rangeStartRange.compareBoundaryPoints(
26431 Range.START_TO_START, nodeEndRange) == -1 &&
26432 rangeEndRange.compareBoundaryPoints(
26433 Range.START_TO_START, nodeStartRange) == 1;
26437 rangeCompareNode : function(range, node)
26439 var nodeRange = node.ownerDocument.createRange();
26441 nodeRange.selectNode(node);
26443 nodeRange.selectNodeContents(node);
26447 range.collapse(true);
26449 nodeRange.collapse(true);
26451 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26452 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26454 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26456 var nodeIsBefore = ss == 1;
26457 var nodeIsAfter = ee == -1;
26459 if (nodeIsBefore && nodeIsAfter) {
26462 if (!nodeIsBefore && nodeIsAfter) {
26463 return 1; //right trailed.
26466 if (nodeIsBefore && !nodeIsAfter) {
26467 return 2; // left trailed.
26473 // private? - in a new class?
26474 cleanUpPaste : function()
26476 // cleans up the whole document..
26477 Roo.log('cleanuppaste');
26479 this.cleanUpChildren(this.doc.body);
26480 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26481 if (clean != this.doc.body.innerHTML) {
26482 this.doc.body.innerHTML = clean;
26487 cleanWordChars : function(input) {// change the chars to hex code
26488 var he = Roo.HtmlEditorCore;
26490 var output = input;
26491 Roo.each(he.swapCodes, function(sw) {
26492 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26494 output = output.replace(swapper, sw[1]);
26501 cleanUpChildren : function (n)
26503 if (!n.childNodes.length) {
26506 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26507 this.cleanUpChild(n.childNodes[i]);
26514 cleanUpChild : function (node)
26517 //console.log(node);
26518 if (node.nodeName == "#text") {
26519 // clean up silly Windows -- stuff?
26522 if (node.nodeName == "#comment") {
26523 node.parentNode.removeChild(node);
26524 // clean up silly Windows -- stuff?
26527 var lcname = node.tagName.toLowerCase();
26528 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26529 // whitelist of tags..
26531 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26533 node.parentNode.removeChild(node);
26538 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26540 // spans with no attributes - just remove them..
26541 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26542 remove_keep_children = true;
26545 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26546 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26548 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26549 // remove_keep_children = true;
26552 if (remove_keep_children) {
26553 this.cleanUpChildren(node);
26554 // inserts everything just before this node...
26555 while (node.childNodes.length) {
26556 var cn = node.childNodes[0];
26557 node.removeChild(cn);
26558 node.parentNode.insertBefore(cn, node);
26560 node.parentNode.removeChild(node);
26564 if (!node.attributes || !node.attributes.length) {
26569 this.cleanUpChildren(node);
26573 function cleanAttr(n,v)
26576 if (v.match(/^\./) || v.match(/^\//)) {
26579 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26582 if (v.match(/^#/)) {
26585 if (v.match(/^\{/)) { // allow template editing.
26588 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26589 node.removeAttribute(n);
26593 var cwhite = this.cwhite;
26594 var cblack = this.cblack;
26596 function cleanStyle(n,v)
26598 if (v.match(/expression/)) { //XSS?? should we even bother..
26599 node.removeAttribute(n);
26603 var parts = v.split(/;/);
26606 Roo.each(parts, function(p) {
26607 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26611 var l = p.split(':').shift().replace(/\s+/g,'');
26612 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26614 if ( cwhite.length && cblack.indexOf(l) > -1) {
26615 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26616 //node.removeAttribute(n);
26620 // only allow 'c whitelisted system attributes'
26621 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26622 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26623 //node.removeAttribute(n);
26633 if (clean.length) {
26634 node.setAttribute(n, clean.join(';'));
26636 node.removeAttribute(n);
26642 for (var i = node.attributes.length-1; i > -1 ; i--) {
26643 var a = node.attributes[i];
26646 if (a.name.toLowerCase().substr(0,2)=='on') {
26647 node.removeAttribute(a.name);
26650 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26651 node.removeAttribute(a.name);
26654 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26655 cleanAttr(a.name,a.value); // fixme..
26658 if (a.name == 'style') {
26659 cleanStyle(a.name,a.value);
26662 /// clean up MS crap..
26663 // tecnically this should be a list of valid class'es..
26666 if (a.name == 'class') {
26667 if (a.value.match(/^Mso/)) {
26668 node.removeAttribute('class');
26671 if (a.value.match(/^body$/)) {
26672 node.removeAttribute('class');
26683 this.cleanUpChildren(node);
26689 * Clean up MS wordisms...
26691 cleanWord : function(node)
26694 this.cleanWord(this.doc.body);
26699 node.nodeName == 'SPAN' &&
26700 !node.hasAttributes() &&
26701 node.childNodes.length == 1 &&
26702 node.firstChild.nodeName == "#text"
26704 var textNode = node.firstChild;
26705 node.removeChild(textNode);
26706 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26707 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26709 node.parentNode.insertBefore(textNode, node);
26710 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26711 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26713 node.parentNode.removeChild(node);
26716 if (node.nodeName == "#text") {
26717 // clean up silly Windows -- stuff?
26720 if (node.nodeName == "#comment") {
26721 node.parentNode.removeChild(node);
26722 // clean up silly Windows -- stuff?
26726 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26727 node.parentNode.removeChild(node);
26730 //Roo.log(node.tagName);
26731 // remove - but keep children..
26732 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26733 //Roo.log('-- removed');
26734 while (node.childNodes.length) {
26735 var cn = node.childNodes[0];
26736 node.removeChild(cn);
26737 node.parentNode.insertBefore(cn, node);
26738 // move node to parent - and clean it..
26739 this.cleanWord(cn);
26741 node.parentNode.removeChild(node);
26742 /// no need to iterate chidlren = it's got none..
26743 //this.iterateChildren(node, this.cleanWord);
26747 if (node.className.length) {
26749 var cn = node.className.split(/\W+/);
26751 Roo.each(cn, function(cls) {
26752 if (cls.match(/Mso[a-zA-Z]+/)) {
26757 node.className = cna.length ? cna.join(' ') : '';
26759 node.removeAttribute("class");
26763 if (node.hasAttribute("lang")) {
26764 node.removeAttribute("lang");
26767 if (node.hasAttribute("style")) {
26769 var styles = node.getAttribute("style").split(";");
26771 Roo.each(styles, function(s) {
26772 if (!s.match(/:/)) {
26775 var kv = s.split(":");
26776 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26779 // what ever is left... we allow.
26782 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26783 if (!nstyle.length) {
26784 node.removeAttribute('style');
26787 this.iterateChildren(node, this.cleanWord);
26793 * iterateChildren of a Node, calling fn each time, using this as the scole..
26794 * @param {DomNode} node node to iterate children of.
26795 * @param {Function} fn method of this class to call on each item.
26797 iterateChildren : function(node, fn)
26799 if (!node.childNodes.length) {
26802 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26803 fn.call(this, node.childNodes[i])
26809 * cleanTableWidths.
26811 * Quite often pasting from word etc.. results in tables with column and widths.
26812 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26815 cleanTableWidths : function(node)
26820 this.cleanTableWidths(this.doc.body);
26825 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26828 Roo.log(node.tagName);
26829 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26830 this.iterateChildren(node, this.cleanTableWidths);
26833 if (node.hasAttribute('width')) {
26834 node.removeAttribute('width');
26838 if (node.hasAttribute("style")) {
26841 var styles = node.getAttribute("style").split(";");
26843 Roo.each(styles, function(s) {
26844 if (!s.match(/:/)) {
26847 var kv = s.split(":");
26848 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26851 // what ever is left... we allow.
26854 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26855 if (!nstyle.length) {
26856 node.removeAttribute('style');
26860 this.iterateChildren(node, this.cleanTableWidths);
26868 domToHTML : function(currentElement, depth, nopadtext) {
26870 depth = depth || 0;
26871 nopadtext = nopadtext || false;
26873 if (!currentElement) {
26874 return this.domToHTML(this.doc.body);
26877 //Roo.log(currentElement);
26879 var allText = false;
26880 var nodeName = currentElement.nodeName;
26881 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26883 if (nodeName == '#text') {
26885 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26890 if (nodeName != 'BODY') {
26893 // Prints the node tagName, such as <A>, <IMG>, etc
26896 for(i = 0; i < currentElement.attributes.length;i++) {
26898 var aname = currentElement.attributes.item(i).name;
26899 if (!currentElement.attributes.item(i).value.length) {
26902 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26905 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26914 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26917 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26922 // Traverse the tree
26924 var currentElementChild = currentElement.childNodes.item(i);
26925 var allText = true;
26926 var innerHTML = '';
26928 while (currentElementChild) {
26929 // Formatting code (indent the tree so it looks nice on the screen)
26930 var nopad = nopadtext;
26931 if (lastnode == 'SPAN') {
26935 if (currentElementChild.nodeName == '#text') {
26936 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26937 toadd = nopadtext ? toadd : toadd.trim();
26938 if (!nopad && toadd.length > 80) {
26939 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26941 innerHTML += toadd;
26944 currentElementChild = currentElement.childNodes.item(i);
26950 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26952 // Recursively traverse the tree structure of the child node
26953 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26954 lastnode = currentElementChild.nodeName;
26956 currentElementChild=currentElement.childNodes.item(i);
26962 // The remaining code is mostly for formatting the tree
26963 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26968 ret+= "</"+tagName+">";
26974 applyBlacklists : function()
26976 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26977 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26981 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26982 if (b.indexOf(tag) > -1) {
26985 this.white.push(tag);
26989 Roo.each(w, function(tag) {
26990 if (b.indexOf(tag) > -1) {
26993 if (this.white.indexOf(tag) > -1) {
26996 this.white.push(tag);
27001 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27002 if (w.indexOf(tag) > -1) {
27005 this.black.push(tag);
27009 Roo.each(b, function(tag) {
27010 if (w.indexOf(tag) > -1) {
27013 if (this.black.indexOf(tag) > -1) {
27016 this.black.push(tag);
27021 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27022 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27026 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27027 if (b.indexOf(tag) > -1) {
27030 this.cwhite.push(tag);
27034 Roo.each(w, function(tag) {
27035 if (b.indexOf(tag) > -1) {
27038 if (this.cwhite.indexOf(tag) > -1) {
27041 this.cwhite.push(tag);
27046 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27047 if (w.indexOf(tag) > -1) {
27050 this.cblack.push(tag);
27054 Roo.each(b, function(tag) {
27055 if (w.indexOf(tag) > -1) {
27058 if (this.cblack.indexOf(tag) > -1) {
27061 this.cblack.push(tag);
27066 setStylesheets : function(stylesheets)
27068 if(typeof(stylesheets) == 'string'){
27069 Roo.get(this.iframe.contentDocument.head).createChild({
27071 rel : 'stylesheet',
27080 Roo.each(stylesheets, function(s) {
27085 Roo.get(_this.iframe.contentDocument.head).createChild({
27087 rel : 'stylesheet',
27096 removeStylesheets : function()
27100 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27105 setStyle : function(style)
27107 Roo.get(this.iframe.contentDocument.head).createChild({
27116 // hide stuff that is not compatible
27130 * @event specialkey
27134 * @cfg {String} fieldClass @hide
27137 * @cfg {String} focusClass @hide
27140 * @cfg {String} autoCreate @hide
27143 * @cfg {String} inputType @hide
27146 * @cfg {String} invalidClass @hide
27149 * @cfg {String} invalidText @hide
27152 * @cfg {String} msgFx @hide
27155 * @cfg {String} validateOnBlur @hide
27159 Roo.HtmlEditorCore.white = [
27160 'area', 'br', 'img', 'input', 'hr', 'wbr',
27162 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27163 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27164 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27165 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27166 'table', 'ul', 'xmp',
27168 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27171 'dir', 'menu', 'ol', 'ul', 'dl',
27177 Roo.HtmlEditorCore.black = [
27178 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27180 'base', 'basefont', 'bgsound', 'blink', 'body',
27181 'frame', 'frameset', 'head', 'html', 'ilayer',
27182 'iframe', 'layer', 'link', 'meta', 'object',
27183 'script', 'style' ,'title', 'xml' // clean later..
27185 Roo.HtmlEditorCore.clean = [
27186 'script', 'style', 'title', 'xml'
27188 Roo.HtmlEditorCore.remove = [
27193 Roo.HtmlEditorCore.ablack = [
27197 Roo.HtmlEditorCore.aclean = [
27198 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27202 Roo.HtmlEditorCore.pwhite= [
27203 'http', 'https', 'mailto'
27206 // white listed style attributes.
27207 Roo.HtmlEditorCore.cwhite= [
27208 // 'text-align', /// default is to allow most things..
27214 // black listed style attributes.
27215 Roo.HtmlEditorCore.cblack= [
27216 // 'font-size' -- this can be set by the project
27220 Roo.HtmlEditorCore.swapCodes =[
27221 [ 8211, "–" ],
27222 [ 8212, "—" ],
27239 * @class Roo.bootstrap.HtmlEditor
27240 * @extends Roo.bootstrap.TextArea
27241 * Bootstrap HtmlEditor class
27244 * Create a new HtmlEditor
27245 * @param {Object} config The config object
27248 Roo.bootstrap.HtmlEditor = function(config){
27249 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27250 if (!this.toolbars) {
27251 this.toolbars = [];
27254 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27257 * @event initialize
27258 * Fires when the editor is fully initialized (including the iframe)
27259 * @param {HtmlEditor} this
27264 * Fires when the editor is first receives the focus. Any insertion must wait
27265 * until after this event.
27266 * @param {HtmlEditor} this
27270 * @event beforesync
27271 * Fires before the textarea is updated with content from the editor iframe. Return false
27272 * to cancel the sync.
27273 * @param {HtmlEditor} this
27274 * @param {String} html
27278 * @event beforepush
27279 * Fires before the iframe editor is updated with content from the textarea. Return false
27280 * to cancel the push.
27281 * @param {HtmlEditor} this
27282 * @param {String} html
27287 * Fires when the textarea is updated with content from the editor iframe.
27288 * @param {HtmlEditor} this
27289 * @param {String} html
27294 * Fires when the iframe editor is updated with content from the textarea.
27295 * @param {HtmlEditor} this
27296 * @param {String} html
27300 * @event editmodechange
27301 * Fires when the editor switches edit modes
27302 * @param {HtmlEditor} this
27303 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27305 editmodechange: true,
27307 * @event editorevent
27308 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27309 * @param {HtmlEditor} this
27313 * @event firstfocus
27314 * Fires when on first focus - needed by toolbars..
27315 * @param {HtmlEditor} this
27320 * Auto save the htmlEditor value as a file into Events
27321 * @param {HtmlEditor} this
27325 * @event savedpreview
27326 * preview the saved version of htmlEditor
27327 * @param {HtmlEditor} this
27334 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27338 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27343 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27348 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27353 * @cfg {Number} height (in pixels)
27357 * @cfg {Number} width (in pixels)
27362 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27365 stylesheets: false,
27370 // private properties
27371 validationEvent : false,
27373 initialized : false,
27376 onFocus : Roo.emptyFn,
27378 hideMode:'offsets',
27380 tbContainer : false,
27384 toolbarContainer :function() {
27385 return this.wrap.select('.x-html-editor-tb',true).first();
27389 * Protected method that will not generally be called directly. It
27390 * is called when the editor creates its toolbar. Override this method if you need to
27391 * add custom toolbar buttons.
27392 * @param {HtmlEditor} editor
27394 createToolbar : function(){
27395 Roo.log('renewing');
27396 Roo.log("create toolbars");
27398 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27399 this.toolbars[0].render(this.toolbarContainer());
27403 // if (!editor.toolbars || !editor.toolbars.length) {
27404 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27407 // for (var i =0 ; i < editor.toolbars.length;i++) {
27408 // editor.toolbars[i] = Roo.factory(
27409 // typeof(editor.toolbars[i]) == 'string' ?
27410 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27411 // Roo.bootstrap.HtmlEditor);
27412 // editor.toolbars[i].init(editor);
27418 onRender : function(ct, position)
27420 // Roo.log("Call onRender: " + this.xtype);
27422 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27424 this.wrap = this.inputEl().wrap({
27425 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27428 this.editorcore.onRender(ct, position);
27430 if (this.resizable) {
27431 this.resizeEl = new Roo.Resizable(this.wrap, {
27435 minHeight : this.height,
27436 height: this.height,
27437 handles : this.resizable,
27440 resize : function(r, w, h) {
27441 _t.onResize(w,h); // -something
27447 this.createToolbar(this);
27450 if(!this.width && this.resizable){
27451 this.setSize(this.wrap.getSize());
27453 if (this.resizeEl) {
27454 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27455 // should trigger onReize..
27461 onResize : function(w, h)
27463 Roo.log('resize: ' +w + ',' + h );
27464 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27468 if(this.inputEl() ){
27469 if(typeof w == 'number'){
27470 var aw = w - this.wrap.getFrameWidth('lr');
27471 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27474 if(typeof h == 'number'){
27475 var tbh = -11; // fixme it needs to tool bar size!
27476 for (var i =0; i < this.toolbars.length;i++) {
27477 // fixme - ask toolbars for heights?
27478 tbh += this.toolbars[i].el.getHeight();
27479 //if (this.toolbars[i].footer) {
27480 // tbh += this.toolbars[i].footer.el.getHeight();
27488 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27489 ah -= 5; // knock a few pixes off for look..
27490 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27494 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27495 this.editorcore.onResize(ew,eh);
27500 * Toggles the editor between standard and source edit mode.
27501 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27503 toggleSourceEdit : function(sourceEditMode)
27505 this.editorcore.toggleSourceEdit(sourceEditMode);
27507 if(this.editorcore.sourceEditMode){
27508 Roo.log('editor - showing textarea');
27511 // Roo.log(this.syncValue());
27513 this.inputEl().removeClass(['hide', 'x-hidden']);
27514 this.inputEl().dom.removeAttribute('tabIndex');
27515 this.inputEl().focus();
27517 Roo.log('editor - hiding textarea');
27519 // Roo.log(this.pushValue());
27522 this.inputEl().addClass(['hide', 'x-hidden']);
27523 this.inputEl().dom.setAttribute('tabIndex', -1);
27524 //this.deferFocus();
27527 if(this.resizable){
27528 this.setSize(this.wrap.getSize());
27531 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27534 // private (for BoxComponent)
27535 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27537 // private (for BoxComponent)
27538 getResizeEl : function(){
27542 // private (for BoxComponent)
27543 getPositionEl : function(){
27548 initEvents : function(){
27549 this.originalValue = this.getValue();
27553 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27556 // markInvalid : Roo.emptyFn,
27558 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27561 // clearInvalid : Roo.emptyFn,
27563 setValue : function(v){
27564 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27565 this.editorcore.pushValue();
27570 deferFocus : function(){
27571 this.focus.defer(10, this);
27575 focus : function(){
27576 this.editorcore.focus();
27582 onDestroy : function(){
27588 for (var i =0; i < this.toolbars.length;i++) {
27589 // fixme - ask toolbars for heights?
27590 this.toolbars[i].onDestroy();
27593 this.wrap.dom.innerHTML = '';
27594 this.wrap.remove();
27599 onFirstFocus : function(){
27600 //Roo.log("onFirstFocus");
27601 this.editorcore.onFirstFocus();
27602 for (var i =0; i < this.toolbars.length;i++) {
27603 this.toolbars[i].onFirstFocus();
27609 syncValue : function()
27611 this.editorcore.syncValue();
27614 pushValue : function()
27616 this.editorcore.pushValue();
27620 // hide stuff that is not compatible
27634 * @event specialkey
27638 * @cfg {String} fieldClass @hide
27641 * @cfg {String} focusClass @hide
27644 * @cfg {String} autoCreate @hide
27647 * @cfg {String} inputType @hide
27651 * @cfg {String} invalidText @hide
27654 * @cfg {String} msgFx @hide
27657 * @cfg {String} validateOnBlur @hide
27666 Roo.namespace('Roo.bootstrap.htmleditor');
27668 * @class Roo.bootstrap.HtmlEditorToolbar1
27674 new Roo.bootstrap.HtmlEditor({
27677 new Roo.bootstrap.HtmlEditorToolbar1({
27678 disable : { fonts: 1 , format: 1, ..., ... , ...],
27684 * @cfg {Object} disable List of elements to disable..
27685 * @cfg {Array} btns List of additional buttons.
27689 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27692 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27695 Roo.apply(this, config);
27697 // default disabled, based on 'good practice'..
27698 this.disable = this.disable || {};
27699 Roo.applyIf(this.disable, {
27702 specialElements : true
27704 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27706 this.editor = config.editor;
27707 this.editorcore = config.editor.editorcore;
27709 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27711 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27712 // dont call parent... till later.
27714 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27719 editorcore : false,
27724 "h1","h2","h3","h4","h5","h6",
27726 "abbr", "acronym", "address", "cite", "samp", "var",
27730 onRender : function(ct, position)
27732 // Roo.log("Call onRender: " + this.xtype);
27734 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27736 this.el.dom.style.marginBottom = '0';
27738 var editorcore = this.editorcore;
27739 var editor= this.editor;
27742 var btn = function(id,cmd , toggle, handler, html){
27744 var event = toggle ? 'toggle' : 'click';
27749 xns: Roo.bootstrap,
27753 enableToggle:toggle !== false,
27755 pressed : toggle ? false : null,
27758 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27759 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27765 // var cb_box = function...
27770 xns: Roo.bootstrap,
27775 xns: Roo.bootstrap,
27779 Roo.each(this.formats, function(f) {
27780 style.menu.items.push({
27782 xns: Roo.bootstrap,
27783 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27788 editorcore.insertTag(this.tagname);
27795 children.push(style);
27797 btn('bold',false,true);
27798 btn('italic',false,true);
27799 btn('align-left', 'justifyleft',true);
27800 btn('align-center', 'justifycenter',true);
27801 btn('align-right' , 'justifyright',true);
27802 btn('link', false, false, function(btn) {
27803 //Roo.log("create link?");
27804 var url = prompt(this.createLinkText, this.defaultLinkValue);
27805 if(url && url != 'http:/'+'/'){
27806 this.editorcore.relayCmd('createlink', url);
27809 btn('list','insertunorderedlist',true);
27810 btn('pencil', false,true, function(btn){
27812 this.toggleSourceEdit(btn.pressed);
27815 if (this.editor.btns.length > 0) {
27816 for (var i = 0; i<this.editor.btns.length; i++) {
27817 children.push(this.editor.btns[i]);
27825 xns: Roo.bootstrap,
27830 xns: Roo.bootstrap,
27835 cog.menu.items.push({
27837 xns: Roo.bootstrap,
27838 html : Clean styles,
27843 editorcore.insertTag(this.tagname);
27852 this.xtype = 'NavSimplebar';
27854 for(var i=0;i< children.length;i++) {
27856 this.buttons.add(this.addxtypeChild(children[i]));
27860 editor.on('editorevent', this.updateToolbar, this);
27862 onBtnClick : function(id)
27864 this.editorcore.relayCmd(id);
27865 this.editorcore.focus();
27869 * Protected method that will not generally be called directly. It triggers
27870 * a toolbar update by reading the markup state of the current selection in the editor.
27872 updateToolbar: function(){
27874 if(!this.editorcore.activated){
27875 this.editor.onFirstFocus(); // is this neeed?
27879 var btns = this.buttons;
27880 var doc = this.editorcore.doc;
27881 btns.get('bold').setActive(doc.queryCommandState('bold'));
27882 btns.get('italic').setActive(doc.queryCommandState('italic'));
27883 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27885 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27886 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27887 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27889 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27890 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27893 var ans = this.editorcore.getAllAncestors();
27894 if (this.formatCombo) {
27897 var store = this.formatCombo.store;
27898 this.formatCombo.setValue("");
27899 for (var i =0; i < ans.length;i++) {
27900 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27902 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27910 // hides menus... - so this cant be on a menu...
27911 Roo.bootstrap.MenuMgr.hideAll();
27913 Roo.bootstrap.MenuMgr.hideAll();
27914 //this.editorsyncValue();
27916 onFirstFocus: function() {
27917 this.buttons.each(function(item){
27921 toggleSourceEdit : function(sourceEditMode){
27924 if(sourceEditMode){
27925 Roo.log("disabling buttons");
27926 this.buttons.each( function(item){
27927 if(item.cmd != 'pencil'){
27933 Roo.log("enabling buttons");
27934 if(this.editorcore.initialized){
27935 this.buttons.each( function(item){
27941 Roo.log("calling toggole on editor");
27942 // tell the editor that it's been pressed..
27943 this.editor.toggleSourceEdit(sourceEditMode);
27957 * @class Roo.bootstrap.Markdown
27958 * @extends Roo.bootstrap.TextArea
27959 * Bootstrap Showdown editable area
27960 * @cfg {string} content
27963 * Create a new Showdown
27966 Roo.bootstrap.Markdown = function(config){
27967 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27971 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27975 initEvents : function()
27978 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27979 this.markdownEl = this.el.createChild({
27980 cls : 'roo-markdown-area'
27982 this.inputEl().addClass('d-none');
27983 if (this.getValue() == '') {
27984 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27987 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27989 this.markdownEl.on('click', this.toggleTextEdit, this);
27990 this.on('blur', this.toggleTextEdit, this);
27991 this.on('specialkey', this.resizeTextArea, this);
27994 toggleTextEdit : function()
27996 var sh = this.markdownEl.getHeight();
27997 this.inputEl().addClass('d-none');
27998 this.markdownEl.addClass('d-none');
27999 if (!this.editing) {
28001 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28002 this.inputEl().removeClass('d-none');
28003 this.inputEl().focus();
28004 this.editing = true;
28007 // show showdown...
28008 this.updateMarkdown();
28009 this.markdownEl.removeClass('d-none');
28010 this.editing = false;
28013 updateMarkdown : function()
28015 if (this.getValue() == '') {
28016 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28020 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28023 resizeTextArea: function () {
28026 Roo.log([sh, this.getValue().split("\n").length * 30]);
28027 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28029 setValue : function(val)
28031 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28032 if (!this.editing) {
28033 this.updateMarkdown();
28039 if (!this.editing) {
28040 this.toggleTextEdit();
28048 * Ext JS Library 1.1.1
28049 * Copyright(c) 2006-2007, Ext JS, LLC.
28051 * Originally Released Under LGPL - original licence link has changed is not relivant.
28054 * <script type="text/javascript">
28058 * @class Roo.bootstrap.PagingToolbar
28059 * @extends Roo.bootstrap.NavSimplebar
28060 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28062 * Create a new PagingToolbar
28063 * @param {Object} config The config object
28064 * @param {Roo.data.Store} store
28066 Roo.bootstrap.PagingToolbar = function(config)
28068 // old args format still supported... - xtype is prefered..
28069 // created from xtype...
28071 this.ds = config.dataSource;
28073 if (config.store && !this.ds) {
28074 this.store= Roo.factory(config.store, Roo.data);
28075 this.ds = this.store;
28076 this.ds.xmodule = this.xmodule || false;
28079 this.toolbarItems = [];
28080 if (config.items) {
28081 this.toolbarItems = config.items;
28084 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28089 this.bind(this.ds);
28092 if (Roo.bootstrap.version == 4) {
28093 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28095 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28100 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28102 * @cfg {Roo.data.Store} dataSource
28103 * The underlying data store providing the paged data
28106 * @cfg {String/HTMLElement/Element} container
28107 * container The id or element that will contain the toolbar
28110 * @cfg {Boolean} displayInfo
28111 * True to display the displayMsg (defaults to false)
28114 * @cfg {Number} pageSize
28115 * The number of records to display per page (defaults to 20)
28119 * @cfg {String} displayMsg
28120 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28122 displayMsg : 'Displaying {0} - {1} of {2}',
28124 * @cfg {String} emptyMsg
28125 * The message to display when no records are found (defaults to "No data to display")
28127 emptyMsg : 'No data to display',
28129 * Customizable piece of the default paging text (defaults to "Page")
28132 beforePageText : "Page",
28134 * Customizable piece of the default paging text (defaults to "of %0")
28137 afterPageText : "of {0}",
28139 * Customizable piece of the default paging text (defaults to "First Page")
28142 firstText : "First Page",
28144 * Customizable piece of the default paging text (defaults to "Previous Page")
28147 prevText : "Previous Page",
28149 * Customizable piece of the default paging text (defaults to "Next Page")
28152 nextText : "Next Page",
28154 * Customizable piece of the default paging text (defaults to "Last Page")
28157 lastText : "Last Page",
28159 * Customizable piece of the default paging text (defaults to "Refresh")
28162 refreshText : "Refresh",
28166 onRender : function(ct, position)
28168 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28169 this.navgroup.parentId = this.id;
28170 this.navgroup.onRender(this.el, null);
28171 // add the buttons to the navgroup
28173 if(this.displayInfo){
28174 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28175 this.displayEl = this.el.select('.x-paging-info', true).first();
28176 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28177 // this.displayEl = navel.el.select('span',true).first();
28183 Roo.each(_this.buttons, function(e){ // this might need to use render????
28184 Roo.factory(e).render(_this.el);
28188 Roo.each(_this.toolbarItems, function(e) {
28189 _this.navgroup.addItem(e);
28193 this.first = this.navgroup.addItem({
28194 tooltip: this.firstText,
28195 cls: "prev btn-outline-secondary",
28196 html : ' <i class="fa fa-step-backward"></i>',
28198 preventDefault: true,
28199 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28202 this.prev = this.navgroup.addItem({
28203 tooltip: this.prevText,
28204 cls: "prev btn-outline-secondary",
28205 html : ' <i class="fa fa-backward"></i>',
28207 preventDefault: true,
28208 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28210 //this.addSeparator();
28213 var field = this.navgroup.addItem( {
28215 cls : 'x-paging-position btn-outline-secondary',
28217 html : this.beforePageText +
28218 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28219 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28222 this.field = field.el.select('input', true).first();
28223 this.field.on("keydown", this.onPagingKeydown, this);
28224 this.field.on("focus", function(){this.dom.select();});
28227 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28228 //this.field.setHeight(18);
28229 //this.addSeparator();
28230 this.next = this.navgroup.addItem({
28231 tooltip: this.nextText,
28232 cls: "next btn-outline-secondary",
28233 html : ' <i class="fa fa-forward"></i>',
28235 preventDefault: true,
28236 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28238 this.last = this.navgroup.addItem({
28239 tooltip: this.lastText,
28240 html : ' <i class="fa fa-step-forward"></i>',
28241 cls: "next btn-outline-secondary",
28243 preventDefault: true,
28244 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28246 //this.addSeparator();
28247 this.loading = this.navgroup.addItem({
28248 tooltip: this.refreshText,
28249 cls: "btn-outline-secondary",
28250 html : ' <i class="fa fa-refresh"></i>',
28251 preventDefault: true,
28252 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28258 updateInfo : function(){
28259 if(this.displayEl){
28260 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28261 var msg = count == 0 ?
28265 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28267 this.displayEl.update(msg);
28272 onLoad : function(ds, r, o)
28274 this.cursor = o.params && o.params.start ? o.params.start : 0;
28276 var d = this.getPageData(),
28281 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28282 this.field.dom.value = ap;
28283 this.first.setDisabled(ap == 1);
28284 this.prev.setDisabled(ap == 1);
28285 this.next.setDisabled(ap == ps);
28286 this.last.setDisabled(ap == ps);
28287 this.loading.enable();
28292 getPageData : function(){
28293 var total = this.ds.getTotalCount();
28296 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28297 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28302 onLoadError : function(){
28303 this.loading.enable();
28307 onPagingKeydown : function(e){
28308 var k = e.getKey();
28309 var d = this.getPageData();
28311 var v = this.field.dom.value, pageNum;
28312 if(!v || isNaN(pageNum = parseInt(v, 10))){
28313 this.field.dom.value = d.activePage;
28316 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28317 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28320 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))
28322 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28323 this.field.dom.value = pageNum;
28324 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28327 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28329 var v = this.field.dom.value, pageNum;
28330 var increment = (e.shiftKey) ? 10 : 1;
28331 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28334 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28335 this.field.dom.value = d.activePage;
28338 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28340 this.field.dom.value = parseInt(v, 10) + increment;
28341 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28342 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28349 beforeLoad : function(){
28351 this.loading.disable();
28356 onClick : function(which){
28365 ds.load({params:{start: 0, limit: this.pageSize}});
28368 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28371 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28374 var total = ds.getTotalCount();
28375 var extra = total % this.pageSize;
28376 var lastStart = extra ? (total - extra) : total-this.pageSize;
28377 ds.load({params:{start: lastStart, limit: this.pageSize}});
28380 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28386 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28387 * @param {Roo.data.Store} store The data store to unbind
28389 unbind : function(ds){
28390 ds.un("beforeload", this.beforeLoad, this);
28391 ds.un("load", this.onLoad, this);
28392 ds.un("loadexception", this.onLoadError, this);
28393 ds.un("remove", this.updateInfo, this);
28394 ds.un("add", this.updateInfo, this);
28395 this.ds = undefined;
28399 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28400 * @param {Roo.data.Store} store The data store to bind
28402 bind : function(ds){
28403 ds.on("beforeload", this.beforeLoad, this);
28404 ds.on("load", this.onLoad, this);
28405 ds.on("loadexception", this.onLoadError, this);
28406 ds.on("remove", this.updateInfo, this);
28407 ds.on("add", this.updateInfo, this);
28418 * @class Roo.bootstrap.MessageBar
28419 * @extends Roo.bootstrap.Component
28420 * Bootstrap MessageBar class
28421 * @cfg {String} html contents of the MessageBar
28422 * @cfg {String} weight (info | success | warning | danger) default info
28423 * @cfg {String} beforeClass insert the bar before the given class
28424 * @cfg {Boolean} closable (true | false) default false
28425 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28428 * Create a new Element
28429 * @param {Object} config The config object
28432 Roo.bootstrap.MessageBar = function(config){
28433 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28436 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28442 beforeClass: 'bootstrap-sticky-wrap',
28444 getAutoCreate : function(){
28448 cls: 'alert alert-dismissable alert-' + this.weight,
28453 html: this.html || ''
28459 cfg.cls += ' alert-messages-fixed';
28473 onRender : function(ct, position)
28475 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28478 var cfg = Roo.apply({}, this.getAutoCreate());
28482 cfg.cls += ' ' + this.cls;
28485 cfg.style = this.style;
28487 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28489 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28492 this.el.select('>button.close').on('click', this.hide, this);
28498 if (!this.rendered) {
28504 this.fireEvent('show', this);
28510 if (!this.rendered) {
28516 this.fireEvent('hide', this);
28519 update : function()
28521 // var e = this.el.dom.firstChild;
28523 // if(this.closable){
28524 // e = e.nextSibling;
28527 // e.data = this.html || '';
28529 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28545 * @class Roo.bootstrap.Graph
28546 * @extends Roo.bootstrap.Component
28547 * Bootstrap Graph class
28551 @cfg {String} graphtype bar | vbar | pie
28552 @cfg {number} g_x coodinator | centre x (pie)
28553 @cfg {number} g_y coodinator | centre y (pie)
28554 @cfg {number} g_r radius (pie)
28555 @cfg {number} g_height height of the chart (respected by all elements in the set)
28556 @cfg {number} g_width width of the chart (respected by all elements in the set)
28557 @cfg {Object} title The title of the chart
28560 -opts (object) options for the chart
28562 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28563 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28565 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.
28566 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28568 o stretch (boolean)
28570 -opts (object) options for the pie
28573 o startAngle (number)
28574 o endAngle (number)
28578 * Create a new Input
28579 * @param {Object} config The config object
28582 Roo.bootstrap.Graph = function(config){
28583 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28589 * The img click event for the img.
28590 * @param {Roo.EventObject} e
28596 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28607 //g_colors: this.colors,
28614 getAutoCreate : function(){
28625 onRender : function(ct,position){
28628 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28630 if (typeof(Raphael) == 'undefined') {
28631 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28635 this.raphael = Raphael(this.el.dom);
28637 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28638 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28639 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28640 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28642 r.text(160, 10, "Single Series Chart").attr(txtattr);
28643 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28644 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28645 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28647 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28648 r.barchart(330, 10, 300, 220, data1);
28649 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28650 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28653 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28654 // r.barchart(30, 30, 560, 250, xdata, {
28655 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28656 // axis : "0 0 1 1",
28657 // axisxlabels : xdata
28658 // //yvalues : cols,
28661 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28663 // this.load(null,xdata,{
28664 // axis : "0 0 1 1",
28665 // axisxlabels : xdata
28670 load : function(graphtype,xdata,opts)
28672 this.raphael.clear();
28674 graphtype = this.graphtype;
28679 var r = this.raphael,
28680 fin = function () {
28681 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28683 fout = function () {
28684 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28686 pfin = function() {
28687 this.sector.stop();
28688 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28691 this.label[0].stop();
28692 this.label[0].attr({ r: 7.5 });
28693 this.label[1].attr({ "font-weight": 800 });
28696 pfout = function() {
28697 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28700 this.label[0].animate({ r: 5 }, 500, "bounce");
28701 this.label[1].attr({ "font-weight": 400 });
28707 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28710 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28713 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28714 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28716 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28723 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28728 setTitle: function(o)
28733 initEvents: function() {
28736 this.el.on('click', this.onClick, this);
28740 onClick : function(e)
28742 Roo.log('img onclick');
28743 this.fireEvent('click', this, e);
28755 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28758 * @class Roo.bootstrap.dash.NumberBox
28759 * @extends Roo.bootstrap.Component
28760 * Bootstrap NumberBox class
28761 * @cfg {String} headline Box headline
28762 * @cfg {String} content Box content
28763 * @cfg {String} icon Box icon
28764 * @cfg {String} footer Footer text
28765 * @cfg {String} fhref Footer href
28768 * Create a new NumberBox
28769 * @param {Object} config The config object
28773 Roo.bootstrap.dash.NumberBox = function(config){
28774 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28778 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28787 getAutoCreate : function(){
28791 cls : 'small-box ',
28799 cls : 'roo-headline',
28800 html : this.headline
28804 cls : 'roo-content',
28805 html : this.content
28819 cls : 'ion ' + this.icon
28828 cls : 'small-box-footer',
28829 href : this.fhref || '#',
28833 cfg.cn.push(footer);
28840 onRender : function(ct,position){
28841 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28848 setHeadline: function (value)
28850 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28853 setFooter: function (value, href)
28855 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28858 this.el.select('a.small-box-footer',true).first().attr('href', href);
28863 setContent: function (value)
28865 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28868 initEvents: function()
28882 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28885 * @class Roo.bootstrap.dash.TabBox
28886 * @extends Roo.bootstrap.Component
28887 * Bootstrap TabBox class
28888 * @cfg {String} title Title of the TabBox
28889 * @cfg {String} icon Icon of the TabBox
28890 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28891 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28894 * Create a new TabBox
28895 * @param {Object} config The config object
28899 Roo.bootstrap.dash.TabBox = function(config){
28900 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28905 * When a pane is added
28906 * @param {Roo.bootstrap.dash.TabPane} pane
28910 * @event activatepane
28911 * When a pane is activated
28912 * @param {Roo.bootstrap.dash.TabPane} pane
28914 "activatepane" : true
28922 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28927 tabScrollable : false,
28929 getChildContainer : function()
28931 return this.el.select('.tab-content', true).first();
28934 getAutoCreate : function(){
28938 cls: 'pull-left header',
28946 cls: 'fa ' + this.icon
28952 cls: 'nav nav-tabs pull-right',
28958 if(this.tabScrollable){
28965 cls: 'nav nav-tabs pull-right',
28976 cls: 'nav-tabs-custom',
28981 cls: 'tab-content no-padding',
28989 initEvents : function()
28991 //Roo.log('add add pane handler');
28992 this.on('addpane', this.onAddPane, this);
28995 * Updates the box title
28996 * @param {String} html to set the title to.
28998 setTitle : function(value)
29000 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29002 onAddPane : function(pane)
29004 this.panes.push(pane);
29005 //Roo.log('addpane');
29007 // tabs are rendere left to right..
29008 if(!this.showtabs){
29012 var ctr = this.el.select('.nav-tabs', true).first();
29015 var existing = ctr.select('.nav-tab',true);
29016 var qty = existing.getCount();;
29019 var tab = ctr.createChild({
29021 cls : 'nav-tab' + (qty ? '' : ' active'),
29029 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29032 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29034 pane.el.addClass('active');
29039 onTabClick : function(ev,un,ob,pane)
29041 //Roo.log('tab - prev default');
29042 ev.preventDefault();
29045 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29046 pane.tab.addClass('active');
29047 //Roo.log(pane.title);
29048 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29049 // technically we should have a deactivate event.. but maybe add later.
29050 // and it should not de-activate the selected tab...
29051 this.fireEvent('activatepane', pane);
29052 pane.el.addClass('active');
29053 pane.fireEvent('activate');
29058 getActivePane : function()
29061 Roo.each(this.panes, function(p) {
29062 if(p.el.hasClass('active')){
29083 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29085 * @class Roo.bootstrap.TabPane
29086 * @extends Roo.bootstrap.Component
29087 * Bootstrap TabPane class
29088 * @cfg {Boolean} active (false | true) Default false
29089 * @cfg {String} title title of panel
29093 * Create a new TabPane
29094 * @param {Object} config The config object
29097 Roo.bootstrap.dash.TabPane = function(config){
29098 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29104 * When a pane is activated
29105 * @param {Roo.bootstrap.dash.TabPane} pane
29112 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29117 // the tabBox that this is attached to.
29120 getAutoCreate : function()
29128 cfg.cls += ' active';
29133 initEvents : function()
29135 //Roo.log('trigger add pane handler');
29136 this.parent().fireEvent('addpane', this)
29140 * Updates the tab title
29141 * @param {String} html to set the title to.
29143 setTitle: function(str)
29149 this.tab.select('a', true).first().dom.innerHTML = str;
29166 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29169 * @class Roo.bootstrap.menu.Menu
29170 * @extends Roo.bootstrap.Component
29171 * Bootstrap Menu class - container for Menu
29172 * @cfg {String} html Text of the menu
29173 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29174 * @cfg {String} icon Font awesome icon
29175 * @cfg {String} pos Menu align to (top | bottom) default bottom
29179 * Create a new Menu
29180 * @param {Object} config The config object
29184 Roo.bootstrap.menu.Menu = function(config){
29185 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29189 * @event beforeshow
29190 * Fires before this menu is displayed
29191 * @param {Roo.bootstrap.menu.Menu} this
29195 * @event beforehide
29196 * Fires before this menu is hidden
29197 * @param {Roo.bootstrap.menu.Menu} this
29202 * Fires after this menu is displayed
29203 * @param {Roo.bootstrap.menu.Menu} this
29208 * Fires after this menu is hidden
29209 * @param {Roo.bootstrap.menu.Menu} this
29214 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29215 * @param {Roo.bootstrap.menu.Menu} this
29216 * @param {Roo.EventObject} e
29223 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29227 weight : 'default',
29232 getChildContainer : function() {
29233 if(this.isSubMenu){
29237 return this.el.select('ul.dropdown-menu', true).first();
29240 getAutoCreate : function()
29245 cls : 'roo-menu-text',
29253 cls : 'fa ' + this.icon
29264 cls : 'dropdown-button btn btn-' + this.weight,
29269 cls : 'dropdown-toggle btn btn-' + this.weight,
29279 cls : 'dropdown-menu'
29285 if(this.pos == 'top'){
29286 cfg.cls += ' dropup';
29289 if(this.isSubMenu){
29292 cls : 'dropdown-menu'
29299 onRender : function(ct, position)
29301 this.isSubMenu = ct.hasClass('dropdown-submenu');
29303 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29306 initEvents : function()
29308 if(this.isSubMenu){
29312 this.hidden = true;
29314 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29315 this.triggerEl.on('click', this.onTriggerPress, this);
29317 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29318 this.buttonEl.on('click', this.onClick, this);
29324 if(this.isSubMenu){
29328 return this.el.select('ul.dropdown-menu', true).first();
29331 onClick : function(e)
29333 this.fireEvent("click", this, e);
29336 onTriggerPress : function(e)
29338 if (this.isVisible()) {
29345 isVisible : function(){
29346 return !this.hidden;
29351 this.fireEvent("beforeshow", this);
29353 this.hidden = false;
29354 this.el.addClass('open');
29356 Roo.get(document).on("mouseup", this.onMouseUp, this);
29358 this.fireEvent("show", this);
29365 this.fireEvent("beforehide", this);
29367 this.hidden = true;
29368 this.el.removeClass('open');
29370 Roo.get(document).un("mouseup", this.onMouseUp);
29372 this.fireEvent("hide", this);
29375 onMouseUp : function()
29389 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29392 * @class Roo.bootstrap.menu.Item
29393 * @extends Roo.bootstrap.Component
29394 * Bootstrap MenuItem class
29395 * @cfg {Boolean} submenu (true | false) default false
29396 * @cfg {String} html text of the item
29397 * @cfg {String} href the link
29398 * @cfg {Boolean} disable (true | false) default false
29399 * @cfg {Boolean} preventDefault (true | false) default true
29400 * @cfg {String} icon Font awesome icon
29401 * @cfg {String} pos Submenu align to (left | right) default right
29405 * Create a new Item
29406 * @param {Object} config The config object
29410 Roo.bootstrap.menu.Item = function(config){
29411 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29415 * Fires when the mouse is hovering over this menu
29416 * @param {Roo.bootstrap.menu.Item} this
29417 * @param {Roo.EventObject} e
29422 * Fires when the mouse exits this menu
29423 * @param {Roo.bootstrap.menu.Item} this
29424 * @param {Roo.EventObject} e
29430 * The raw click event for the entire grid.
29431 * @param {Roo.EventObject} e
29437 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29442 preventDefault: true,
29447 getAutoCreate : function()
29452 cls : 'roo-menu-item-text',
29460 cls : 'fa ' + this.icon
29469 href : this.href || '#',
29476 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29480 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29482 if(this.pos == 'left'){
29483 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29490 initEvents : function()
29492 this.el.on('mouseover', this.onMouseOver, this);
29493 this.el.on('mouseout', this.onMouseOut, this);
29495 this.el.select('a', true).first().on('click', this.onClick, this);
29499 onClick : function(e)
29501 if(this.preventDefault){
29502 e.preventDefault();
29505 this.fireEvent("click", this, e);
29508 onMouseOver : function(e)
29510 if(this.submenu && this.pos == 'left'){
29511 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29514 this.fireEvent("mouseover", this, e);
29517 onMouseOut : function(e)
29519 this.fireEvent("mouseout", this, e);
29531 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29534 * @class Roo.bootstrap.menu.Separator
29535 * @extends Roo.bootstrap.Component
29536 * Bootstrap Separator class
29539 * Create a new Separator
29540 * @param {Object} config The config object
29544 Roo.bootstrap.menu.Separator = function(config){
29545 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29548 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29550 getAutoCreate : function(){
29553 cls: 'dropdown-divider divider'
29571 * @class Roo.bootstrap.Tooltip
29572 * Bootstrap Tooltip class
29573 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29574 * to determine which dom element triggers the tooltip.
29576 * It needs to add support for additional attributes like tooltip-position
29579 * Create a new Toolti
29580 * @param {Object} config The config object
29583 Roo.bootstrap.Tooltip = function(config){
29584 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29586 this.alignment = Roo.bootstrap.Tooltip.alignment;
29588 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29589 this.alignment = config.alignment;
29594 Roo.apply(Roo.bootstrap.Tooltip, {
29596 * @function init initialize tooltip monitoring.
29600 currentTip : false,
29601 currentRegion : false,
29607 Roo.get(document).on('mouseover', this.enter ,this);
29608 Roo.get(document).on('mouseout', this.leave, this);
29611 this.currentTip = new Roo.bootstrap.Tooltip();
29614 enter : function(ev)
29616 var dom = ev.getTarget();
29618 //Roo.log(['enter',dom]);
29619 var el = Roo.fly(dom);
29620 if (this.currentEl) {
29622 //Roo.log(this.currentEl);
29623 //Roo.log(this.currentEl.contains(dom));
29624 if (this.currentEl == el) {
29627 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29633 if (this.currentTip.el) {
29634 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29638 if(!el || el.dom == document){
29644 if (!el.attr('tooltip')) {
29645 pel = el.findParent("[tooltip]");
29647 bindEl = Roo.get(pel);
29653 // you can not look for children, as if el is the body.. then everythign is the child..
29654 if (!pel && !el.attr('tooltip')) { //
29655 if (!el.select("[tooltip]").elements.length) {
29658 // is the mouse over this child...?
29659 bindEl = el.select("[tooltip]").first();
29660 var xy = ev.getXY();
29661 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29662 //Roo.log("not in region.");
29665 //Roo.log("child element over..");
29668 this.currentEl = el;
29669 this.currentTip.bind(bindEl);
29670 this.currentRegion = Roo.lib.Region.getRegion(dom);
29671 this.currentTip.enter();
29674 leave : function(ev)
29676 var dom = ev.getTarget();
29677 //Roo.log(['leave',dom]);
29678 if (!this.currentEl) {
29683 if (dom != this.currentEl.dom) {
29686 var xy = ev.getXY();
29687 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29690 // only activate leave if mouse cursor is outside... bounding box..
29695 if (this.currentTip) {
29696 this.currentTip.leave();
29698 //Roo.log('clear currentEl');
29699 this.currentEl = false;
29704 'left' : ['r-l', [-2,0], 'right'],
29705 'right' : ['l-r', [2,0], 'left'],
29706 'bottom' : ['t-b', [0,2], 'top'],
29707 'top' : [ 'b-t', [0,-2], 'bottom']
29713 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29718 delay : null, // can be { show : 300 , hide: 500}
29722 hoverState : null, //???
29724 placement : 'bottom',
29728 getAutoCreate : function(){
29735 cls : 'tooltip-arrow arrow'
29738 cls : 'tooltip-inner'
29745 bind : function(el)
29750 initEvents : function()
29752 this.arrowEl = this.el.select('.arrow', true).first();
29753 this.innerEl = this.el.select('.tooltip-inner', true).first();
29756 enter : function () {
29758 if (this.timeout != null) {
29759 clearTimeout(this.timeout);
29762 this.hoverState = 'in';
29763 //Roo.log("enter - show");
29764 if (!this.delay || !this.delay.show) {
29769 this.timeout = setTimeout(function () {
29770 if (_t.hoverState == 'in') {
29773 }, this.delay.show);
29777 clearTimeout(this.timeout);
29779 this.hoverState = 'out';
29780 if (!this.delay || !this.delay.hide) {
29786 this.timeout = setTimeout(function () {
29787 //Roo.log("leave - timeout");
29789 if (_t.hoverState == 'out') {
29791 Roo.bootstrap.Tooltip.currentEl = false;
29796 show : function (msg)
29799 this.render(document.body);
29802 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29804 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29806 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29808 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29809 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29811 var placement = typeof this.placement == 'function' ?
29812 this.placement.call(this, this.el, on_el) :
29815 var autoToken = /\s?auto?\s?/i;
29816 var autoPlace = autoToken.test(placement);
29818 placement = placement.replace(autoToken, '') || 'top';
29822 //this.el.setXY([0,0]);
29824 //this.el.dom.style.display='block';
29826 //this.el.appendTo(on_el);
29828 var p = this.getPosition();
29829 var box = this.el.getBox();
29835 var align = this.alignment[placement];
29837 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29839 if(placement == 'top' || placement == 'bottom'){
29841 placement = 'right';
29844 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29845 placement = 'left';
29848 var scroll = Roo.select('body', true).first().getScroll();
29850 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29854 align = this.alignment[placement];
29856 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29860 var elems = document.getElementsByTagName('div');
29861 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29862 for (var i = 0; i < elems.length; i++) {
29863 var zindex = Number.parseInt(
29864 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29867 if (zindex > highest) {
29874 this.el.dom.style.zIndex = highest;
29876 this.el.alignTo(this.bindEl, align[0],align[1]);
29877 //var arrow = this.el.select('.arrow',true).first();
29878 //arrow.set(align[2],
29880 this.el.addClass(placement);
29881 this.el.addClass("bs-tooltip-"+ placement);
29883 this.el.addClass('in fade show');
29885 this.hoverState = null;
29887 if (this.el.hasClass('fade')) {
29902 //this.el.setXY([0,0]);
29903 this.el.removeClass(['show', 'in']);
29919 * @class Roo.bootstrap.LocationPicker
29920 * @extends Roo.bootstrap.Component
29921 * Bootstrap LocationPicker class
29922 * @cfg {Number} latitude Position when init default 0
29923 * @cfg {Number} longitude Position when init default 0
29924 * @cfg {Number} zoom default 15
29925 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29926 * @cfg {Boolean} mapTypeControl default false
29927 * @cfg {Boolean} disableDoubleClickZoom default false
29928 * @cfg {Boolean} scrollwheel default true
29929 * @cfg {Boolean} streetViewControl default false
29930 * @cfg {Number} radius default 0
29931 * @cfg {String} locationName
29932 * @cfg {Boolean} draggable default true
29933 * @cfg {Boolean} enableAutocomplete default false
29934 * @cfg {Boolean} enableReverseGeocode default true
29935 * @cfg {String} markerTitle
29938 * Create a new LocationPicker
29939 * @param {Object} config The config object
29943 Roo.bootstrap.LocationPicker = function(config){
29945 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29950 * Fires when the picker initialized.
29951 * @param {Roo.bootstrap.LocationPicker} this
29952 * @param {Google Location} location
29956 * @event positionchanged
29957 * Fires when the picker position changed.
29958 * @param {Roo.bootstrap.LocationPicker} this
29959 * @param {Google Location} location
29961 positionchanged : true,
29964 * Fires when the map resize.
29965 * @param {Roo.bootstrap.LocationPicker} this
29970 * Fires when the map show.
29971 * @param {Roo.bootstrap.LocationPicker} this
29976 * Fires when the map hide.
29977 * @param {Roo.bootstrap.LocationPicker} this
29982 * Fires when click the map.
29983 * @param {Roo.bootstrap.LocationPicker} this
29984 * @param {Map event} e
29988 * @event mapRightClick
29989 * Fires when right click the map.
29990 * @param {Roo.bootstrap.LocationPicker} this
29991 * @param {Map event} e
29993 mapRightClick : true,
29995 * @event markerClick
29996 * Fires when click the marker.
29997 * @param {Roo.bootstrap.LocationPicker} this
29998 * @param {Map event} e
30000 markerClick : true,
30002 * @event markerRightClick
30003 * Fires when right click the marker.
30004 * @param {Roo.bootstrap.LocationPicker} this
30005 * @param {Map event} e
30007 markerRightClick : true,
30009 * @event OverlayViewDraw
30010 * Fires when OverlayView Draw
30011 * @param {Roo.bootstrap.LocationPicker} this
30013 OverlayViewDraw : true,
30015 * @event OverlayViewOnAdd
30016 * Fires when OverlayView Draw
30017 * @param {Roo.bootstrap.LocationPicker} this
30019 OverlayViewOnAdd : true,
30021 * @event OverlayViewOnRemove
30022 * Fires when OverlayView Draw
30023 * @param {Roo.bootstrap.LocationPicker} this
30025 OverlayViewOnRemove : true,
30027 * @event OverlayViewShow
30028 * Fires when OverlayView Draw
30029 * @param {Roo.bootstrap.LocationPicker} this
30030 * @param {Pixel} cpx
30032 OverlayViewShow : true,
30034 * @event OverlayViewHide
30035 * Fires when OverlayView Draw
30036 * @param {Roo.bootstrap.LocationPicker} this
30038 OverlayViewHide : true,
30040 * @event loadexception
30041 * Fires when load google lib failed.
30042 * @param {Roo.bootstrap.LocationPicker} this
30044 loadexception : true
30049 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30051 gMapContext: false,
30057 mapTypeControl: false,
30058 disableDoubleClickZoom: false,
30060 streetViewControl: false,
30064 enableAutocomplete: false,
30065 enableReverseGeocode: true,
30068 getAutoCreate: function()
30073 cls: 'roo-location-picker'
30079 initEvents: function(ct, position)
30081 if(!this.el.getWidth() || this.isApplied()){
30085 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30090 initial: function()
30092 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30093 this.fireEvent('loadexception', this);
30097 if(!this.mapTypeId){
30098 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30101 this.gMapContext = this.GMapContext();
30103 this.initOverlayView();
30105 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30109 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30110 _this.setPosition(_this.gMapContext.marker.position);
30113 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30114 _this.fireEvent('mapClick', this, event);
30118 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30119 _this.fireEvent('mapRightClick', this, event);
30123 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30124 _this.fireEvent('markerClick', this, event);
30128 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30129 _this.fireEvent('markerRightClick', this, event);
30133 this.setPosition(this.gMapContext.location);
30135 this.fireEvent('initial', this, this.gMapContext.location);
30138 initOverlayView: function()
30142 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30146 _this.fireEvent('OverlayViewDraw', _this);
30151 _this.fireEvent('OverlayViewOnAdd', _this);
30154 onRemove: function()
30156 _this.fireEvent('OverlayViewOnRemove', _this);
30159 show: function(cpx)
30161 _this.fireEvent('OverlayViewShow', _this, cpx);
30166 _this.fireEvent('OverlayViewHide', _this);
30172 fromLatLngToContainerPixel: function(event)
30174 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30177 isApplied: function()
30179 return this.getGmapContext() == false ? false : true;
30182 getGmapContext: function()
30184 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30187 GMapContext: function()
30189 var position = new google.maps.LatLng(this.latitude, this.longitude);
30191 var _map = new google.maps.Map(this.el.dom, {
30194 mapTypeId: this.mapTypeId,
30195 mapTypeControl: this.mapTypeControl,
30196 disableDoubleClickZoom: this.disableDoubleClickZoom,
30197 scrollwheel: this.scrollwheel,
30198 streetViewControl: this.streetViewControl,
30199 locationName: this.locationName,
30200 draggable: this.draggable,
30201 enableAutocomplete: this.enableAutocomplete,
30202 enableReverseGeocode: this.enableReverseGeocode
30205 var _marker = new google.maps.Marker({
30206 position: position,
30208 title: this.markerTitle,
30209 draggable: this.draggable
30216 location: position,
30217 radius: this.radius,
30218 locationName: this.locationName,
30219 addressComponents: {
30220 formatted_address: null,
30221 addressLine1: null,
30222 addressLine2: null,
30224 streetNumber: null,
30228 stateOrProvince: null
30231 domContainer: this.el.dom,
30232 geodecoder: new google.maps.Geocoder()
30236 drawCircle: function(center, radius, options)
30238 if (this.gMapContext.circle != null) {
30239 this.gMapContext.circle.setMap(null);
30243 options = Roo.apply({}, options, {
30244 strokeColor: "#0000FF",
30245 strokeOpacity: .35,
30247 fillColor: "#0000FF",
30251 options.map = this.gMapContext.map;
30252 options.radius = radius;
30253 options.center = center;
30254 this.gMapContext.circle = new google.maps.Circle(options);
30255 return this.gMapContext.circle;
30261 setPosition: function(location)
30263 this.gMapContext.location = location;
30264 this.gMapContext.marker.setPosition(location);
30265 this.gMapContext.map.panTo(location);
30266 this.drawCircle(location, this.gMapContext.radius, {});
30270 if (this.gMapContext.settings.enableReverseGeocode) {
30271 this.gMapContext.geodecoder.geocode({
30272 latLng: this.gMapContext.location
30273 }, function(results, status) {
30275 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30276 _this.gMapContext.locationName = results[0].formatted_address;
30277 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30279 _this.fireEvent('positionchanged', this, location);
30286 this.fireEvent('positionchanged', this, location);
30291 google.maps.event.trigger(this.gMapContext.map, "resize");
30293 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30295 this.fireEvent('resize', this);
30298 setPositionByLatLng: function(latitude, longitude)
30300 this.setPosition(new google.maps.LatLng(latitude, longitude));
30303 getCurrentPosition: function()
30306 latitude: this.gMapContext.location.lat(),
30307 longitude: this.gMapContext.location.lng()
30311 getAddressName: function()
30313 return this.gMapContext.locationName;
30316 getAddressComponents: function()
30318 return this.gMapContext.addressComponents;
30321 address_component_from_google_geocode: function(address_components)
30325 for (var i = 0; i < address_components.length; i++) {
30326 var component = address_components[i];
30327 if (component.types.indexOf("postal_code") >= 0) {
30328 result.postalCode = component.short_name;
30329 } else if (component.types.indexOf("street_number") >= 0) {
30330 result.streetNumber = component.short_name;
30331 } else if (component.types.indexOf("route") >= 0) {
30332 result.streetName = component.short_name;
30333 } else if (component.types.indexOf("neighborhood") >= 0) {
30334 result.city = component.short_name;
30335 } else if (component.types.indexOf("locality") >= 0) {
30336 result.city = component.short_name;
30337 } else if (component.types.indexOf("sublocality") >= 0) {
30338 result.district = component.short_name;
30339 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30340 result.stateOrProvince = component.short_name;
30341 } else if (component.types.indexOf("country") >= 0) {
30342 result.country = component.short_name;
30346 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30347 result.addressLine2 = "";
30351 setZoomLevel: function(zoom)
30353 this.gMapContext.map.setZoom(zoom);
30366 this.fireEvent('show', this);
30377 this.fireEvent('hide', this);
30382 Roo.apply(Roo.bootstrap.LocationPicker, {
30384 OverlayView : function(map, options)
30386 options = options || {};
30393 * @class Roo.bootstrap.Alert
30394 * @extends Roo.bootstrap.Component
30395 * Bootstrap Alert class - shows an alert area box
30397 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30398 Enter a valid email address
30401 * @cfg {String} title The title of alert
30402 * @cfg {String} html The content of alert
30403 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30404 * @cfg {String} fa font-awesomeicon
30405 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30406 * @cfg {Boolean} close true to show a x closer
30410 * Create a new alert
30411 * @param {Object} config The config object
30415 Roo.bootstrap.Alert = function(config){
30416 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30420 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30426 faicon: false, // BC
30430 getAutoCreate : function()
30442 style : this.close ? '' : 'display:none'
30446 cls : 'roo-alert-icon'
30451 cls : 'roo-alert-title',
30456 cls : 'roo-alert-text',
30463 cfg.cn[0].cls += ' fa ' + this.faicon;
30466 cfg.cn[0].cls += ' fa ' + this.fa;
30470 cfg.cls += ' alert-' + this.weight;
30476 initEvents: function()
30478 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30479 this.titleEl = this.el.select('.roo-alert-title',true).first();
30480 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30481 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30482 if (this.seconds > 0) {
30483 this.hide.defer(this.seconds, this);
30487 * Set the Title Message HTML
30488 * @param {String} html
30490 setTitle : function(str)
30492 this.titleEl.dom.innerHTML = str;
30496 * Set the Body Message HTML
30497 * @param {String} html
30499 setHtml : function(str)
30501 this.htmlEl.dom.innerHTML = str;
30504 * Set the Weight of the alert
30505 * @param {String} (success|info|warning|danger) weight
30508 setWeight : function(weight)
30511 this.el.removeClass('alert-' + this.weight);
30514 this.weight = weight;
30516 this.el.addClass('alert-' + this.weight);
30519 * Set the Icon of the alert
30520 * @param {String} see fontawsome names (name without the 'fa-' bit)
30522 setIcon : function(icon)
30525 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30528 this.faicon = icon;
30530 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30555 * @class Roo.bootstrap.UploadCropbox
30556 * @extends Roo.bootstrap.Component
30557 * Bootstrap UploadCropbox class
30558 * @cfg {String} emptyText show when image has been loaded
30559 * @cfg {String} rotateNotify show when image too small to rotate
30560 * @cfg {Number} errorTimeout default 3000
30561 * @cfg {Number} minWidth default 300
30562 * @cfg {Number} minHeight default 300
30563 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30564 * @cfg {Boolean} isDocument (true|false) default false
30565 * @cfg {String} url action url
30566 * @cfg {String} paramName default 'imageUpload'
30567 * @cfg {String} method default POST
30568 * @cfg {Boolean} loadMask (true|false) default true
30569 * @cfg {Boolean} loadingText default 'Loading...'
30572 * Create a new UploadCropbox
30573 * @param {Object} config The config object
30576 Roo.bootstrap.UploadCropbox = function(config){
30577 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30581 * @event beforeselectfile
30582 * Fire before select file
30583 * @param {Roo.bootstrap.UploadCropbox} this
30585 "beforeselectfile" : true,
30588 * Fire after initEvent
30589 * @param {Roo.bootstrap.UploadCropbox} this
30594 * Fire after initEvent
30595 * @param {Roo.bootstrap.UploadCropbox} this
30596 * @param {String} data
30601 * Fire when preparing the file data
30602 * @param {Roo.bootstrap.UploadCropbox} this
30603 * @param {Object} file
30608 * Fire when get exception
30609 * @param {Roo.bootstrap.UploadCropbox} this
30610 * @param {XMLHttpRequest} xhr
30612 "exception" : true,
30614 * @event beforeloadcanvas
30615 * Fire before load the canvas
30616 * @param {Roo.bootstrap.UploadCropbox} this
30617 * @param {String} src
30619 "beforeloadcanvas" : true,
30622 * Fire when trash image
30623 * @param {Roo.bootstrap.UploadCropbox} this
30628 * Fire when download the image
30629 * @param {Roo.bootstrap.UploadCropbox} this
30633 * @event footerbuttonclick
30634 * Fire when footerbuttonclick
30635 * @param {Roo.bootstrap.UploadCropbox} this
30636 * @param {String} type
30638 "footerbuttonclick" : true,
30642 * @param {Roo.bootstrap.UploadCropbox} this
30647 * Fire when rotate the image
30648 * @param {Roo.bootstrap.UploadCropbox} this
30649 * @param {String} pos
30654 * Fire when inspect the file
30655 * @param {Roo.bootstrap.UploadCropbox} this
30656 * @param {Object} file
30661 * Fire when xhr upload the file
30662 * @param {Roo.bootstrap.UploadCropbox} this
30663 * @param {Object} data
30668 * Fire when arrange the file data
30669 * @param {Roo.bootstrap.UploadCropbox} this
30670 * @param {Object} formData
30675 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30678 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30680 emptyText : 'Click to upload image',
30681 rotateNotify : 'Image is too small to rotate',
30682 errorTimeout : 3000,
30696 cropType : 'image/jpeg',
30698 canvasLoaded : false,
30699 isDocument : false,
30701 paramName : 'imageUpload',
30703 loadingText : 'Loading...',
30706 getAutoCreate : function()
30710 cls : 'roo-upload-cropbox',
30714 cls : 'roo-upload-cropbox-selector',
30719 cls : 'roo-upload-cropbox-body',
30720 style : 'cursor:pointer',
30724 cls : 'roo-upload-cropbox-preview'
30728 cls : 'roo-upload-cropbox-thumb'
30732 cls : 'roo-upload-cropbox-empty-notify',
30733 html : this.emptyText
30737 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30738 html : this.rotateNotify
30744 cls : 'roo-upload-cropbox-footer',
30747 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30757 onRender : function(ct, position)
30759 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30761 if (this.buttons.length) {
30763 Roo.each(this.buttons, function(bb) {
30765 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30767 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30773 this.maskEl = this.el;
30777 initEvents : function()
30779 this.urlAPI = (window.createObjectURL && window) ||
30780 (window.URL && URL.revokeObjectURL && URL) ||
30781 (window.webkitURL && webkitURL);
30783 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30784 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30786 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30787 this.selectorEl.hide();
30789 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30790 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30792 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30793 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30794 this.thumbEl.hide();
30796 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30797 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30799 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30800 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30801 this.errorEl.hide();
30803 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30804 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30805 this.footerEl.hide();
30807 this.setThumbBoxSize();
30813 this.fireEvent('initial', this);
30820 window.addEventListener("resize", function() { _this.resize(); } );
30822 this.bodyEl.on('click', this.beforeSelectFile, this);
30825 this.bodyEl.on('touchstart', this.onTouchStart, this);
30826 this.bodyEl.on('touchmove', this.onTouchMove, this);
30827 this.bodyEl.on('touchend', this.onTouchEnd, this);
30831 this.bodyEl.on('mousedown', this.onMouseDown, this);
30832 this.bodyEl.on('mousemove', this.onMouseMove, this);
30833 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30834 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30835 Roo.get(document).on('mouseup', this.onMouseUp, this);
30838 this.selectorEl.on('change', this.onFileSelected, this);
30844 this.baseScale = 1;
30846 this.baseRotate = 1;
30847 this.dragable = false;
30848 this.pinching = false;
30851 this.cropData = false;
30852 this.notifyEl.dom.innerHTML = this.emptyText;
30854 this.selectorEl.dom.value = '';
30858 resize : function()
30860 if(this.fireEvent('resize', this) != false){
30861 this.setThumbBoxPosition();
30862 this.setCanvasPosition();
30866 onFooterButtonClick : function(e, el, o, type)
30869 case 'rotate-left' :
30870 this.onRotateLeft(e);
30872 case 'rotate-right' :
30873 this.onRotateRight(e);
30876 this.beforeSelectFile(e);
30891 this.fireEvent('footerbuttonclick', this, type);
30894 beforeSelectFile : function(e)
30896 e.preventDefault();
30898 if(this.fireEvent('beforeselectfile', this) != false){
30899 this.selectorEl.dom.click();
30903 onFileSelected : function(e)
30905 e.preventDefault();
30907 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30911 var file = this.selectorEl.dom.files[0];
30913 if(this.fireEvent('inspect', this, file) != false){
30914 this.prepare(file);
30919 trash : function(e)
30921 this.fireEvent('trash', this);
30924 download : function(e)
30926 this.fireEvent('download', this);
30929 loadCanvas : function(src)
30931 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30935 this.imageEl = document.createElement('img');
30939 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30941 this.imageEl.src = src;
30945 onLoadCanvas : function()
30947 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30948 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30950 this.bodyEl.un('click', this.beforeSelectFile, this);
30952 this.notifyEl.hide();
30953 this.thumbEl.show();
30954 this.footerEl.show();
30956 this.baseRotateLevel();
30958 if(this.isDocument){
30959 this.setThumbBoxSize();
30962 this.setThumbBoxPosition();
30964 this.baseScaleLevel();
30970 this.canvasLoaded = true;
30973 this.maskEl.unmask();
30978 setCanvasPosition : function()
30980 if(!this.canvasEl){
30984 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30985 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30987 this.previewEl.setLeft(pw);
30988 this.previewEl.setTop(ph);
30992 onMouseDown : function(e)
30996 this.dragable = true;
30997 this.pinching = false;
30999 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31000 this.dragable = false;
31004 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31005 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31009 onMouseMove : function(e)
31013 if(!this.canvasLoaded){
31017 if (!this.dragable){
31021 var minX = Math.ceil(this.thumbEl.getLeft(true));
31022 var minY = Math.ceil(this.thumbEl.getTop(true));
31024 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31025 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31027 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31028 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31030 x = x - this.mouseX;
31031 y = y - this.mouseY;
31033 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31034 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31036 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31037 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31039 this.previewEl.setLeft(bgX);
31040 this.previewEl.setTop(bgY);
31042 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31043 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31046 onMouseUp : function(e)
31050 this.dragable = false;
31053 onMouseWheel : function(e)
31057 this.startScale = this.scale;
31059 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31061 if(!this.zoomable()){
31062 this.scale = this.startScale;
31071 zoomable : function()
31073 var minScale = this.thumbEl.getWidth() / this.minWidth;
31075 if(this.minWidth < this.minHeight){
31076 minScale = this.thumbEl.getHeight() / this.minHeight;
31079 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31080 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31084 (this.rotate == 0 || this.rotate == 180) &&
31086 width > this.imageEl.OriginWidth ||
31087 height > this.imageEl.OriginHeight ||
31088 (width < this.minWidth && height < this.minHeight)
31096 (this.rotate == 90 || this.rotate == 270) &&
31098 width > this.imageEl.OriginWidth ||
31099 height > this.imageEl.OriginHeight ||
31100 (width < this.minHeight && height < this.minWidth)
31107 !this.isDocument &&
31108 (this.rotate == 0 || this.rotate == 180) &&
31110 width < this.minWidth ||
31111 width > this.imageEl.OriginWidth ||
31112 height < this.minHeight ||
31113 height > this.imageEl.OriginHeight
31120 !this.isDocument &&
31121 (this.rotate == 90 || this.rotate == 270) &&
31123 width < this.minHeight ||
31124 width > this.imageEl.OriginWidth ||
31125 height < this.minWidth ||
31126 height > this.imageEl.OriginHeight
31136 onRotateLeft : function(e)
31138 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31140 var minScale = this.thumbEl.getWidth() / this.minWidth;
31142 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31143 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31145 this.startScale = this.scale;
31147 while (this.getScaleLevel() < minScale){
31149 this.scale = this.scale + 1;
31151 if(!this.zoomable()){
31156 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31157 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31162 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31169 this.scale = this.startScale;
31171 this.onRotateFail();
31176 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31178 if(this.isDocument){
31179 this.setThumbBoxSize();
31180 this.setThumbBoxPosition();
31181 this.setCanvasPosition();
31186 this.fireEvent('rotate', this, 'left');
31190 onRotateRight : function(e)
31192 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31194 var minScale = this.thumbEl.getWidth() / this.minWidth;
31196 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31197 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31199 this.startScale = this.scale;
31201 while (this.getScaleLevel() < minScale){
31203 this.scale = this.scale + 1;
31205 if(!this.zoomable()){
31210 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31211 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31216 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31223 this.scale = this.startScale;
31225 this.onRotateFail();
31230 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31232 if(this.isDocument){
31233 this.setThumbBoxSize();
31234 this.setThumbBoxPosition();
31235 this.setCanvasPosition();
31240 this.fireEvent('rotate', this, 'right');
31243 onRotateFail : function()
31245 this.errorEl.show(true);
31249 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31254 this.previewEl.dom.innerHTML = '';
31256 var canvasEl = document.createElement("canvas");
31258 var contextEl = canvasEl.getContext("2d");
31260 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31261 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31262 var center = this.imageEl.OriginWidth / 2;
31264 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31265 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31266 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31267 center = this.imageEl.OriginHeight / 2;
31270 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31272 contextEl.translate(center, center);
31273 contextEl.rotate(this.rotate * Math.PI / 180);
31275 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31277 this.canvasEl = document.createElement("canvas");
31279 this.contextEl = this.canvasEl.getContext("2d");
31281 switch (this.rotate) {
31284 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31285 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31287 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31292 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31293 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31295 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31296 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);
31300 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31305 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31306 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31308 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31309 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);
31313 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);
31318 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31319 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31321 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31322 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31326 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);
31333 this.previewEl.appendChild(this.canvasEl);
31335 this.setCanvasPosition();
31340 if(!this.canvasLoaded){
31344 var imageCanvas = document.createElement("canvas");
31346 var imageContext = imageCanvas.getContext("2d");
31348 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31349 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31351 var center = imageCanvas.width / 2;
31353 imageContext.translate(center, center);
31355 imageContext.rotate(this.rotate * Math.PI / 180);
31357 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31359 var canvas = document.createElement("canvas");
31361 var context = canvas.getContext("2d");
31363 canvas.width = this.minWidth;
31364 canvas.height = this.minHeight;
31366 switch (this.rotate) {
31369 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31370 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31372 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31373 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31375 var targetWidth = this.minWidth - 2 * x;
31376 var targetHeight = this.minHeight - 2 * y;
31380 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31381 scale = targetWidth / width;
31384 if(x > 0 && y == 0){
31385 scale = targetHeight / height;
31388 if(x > 0 && y > 0){
31389 scale = targetWidth / width;
31391 if(width < height){
31392 scale = targetHeight / height;
31396 context.scale(scale, scale);
31398 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31399 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31401 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31402 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31404 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31409 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31410 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31412 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31413 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31415 var targetWidth = this.minWidth - 2 * x;
31416 var targetHeight = this.minHeight - 2 * y;
31420 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31421 scale = targetWidth / width;
31424 if(x > 0 && y == 0){
31425 scale = targetHeight / height;
31428 if(x > 0 && y > 0){
31429 scale = targetWidth / width;
31431 if(width < height){
31432 scale = targetHeight / height;
31436 context.scale(scale, scale);
31438 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31439 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31441 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31442 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31444 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31446 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31451 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31452 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31454 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31455 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31457 var targetWidth = this.minWidth - 2 * x;
31458 var targetHeight = this.minHeight - 2 * y;
31462 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31463 scale = targetWidth / width;
31466 if(x > 0 && y == 0){
31467 scale = targetHeight / height;
31470 if(x > 0 && y > 0){
31471 scale = targetWidth / width;
31473 if(width < height){
31474 scale = targetHeight / height;
31478 context.scale(scale, scale);
31480 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31481 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31483 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31484 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31486 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31487 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31489 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31494 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31495 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31497 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31498 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31500 var targetWidth = this.minWidth - 2 * x;
31501 var targetHeight = this.minHeight - 2 * y;
31505 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31506 scale = targetWidth / width;
31509 if(x > 0 && y == 0){
31510 scale = targetHeight / height;
31513 if(x > 0 && y > 0){
31514 scale = targetWidth / width;
31516 if(width < height){
31517 scale = targetHeight / height;
31521 context.scale(scale, scale);
31523 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31524 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31526 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31527 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31529 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31531 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31538 this.cropData = canvas.toDataURL(this.cropType);
31540 if(this.fireEvent('crop', this, this.cropData) !== false){
31541 this.process(this.file, this.cropData);
31548 setThumbBoxSize : function()
31552 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31553 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31554 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31556 this.minWidth = width;
31557 this.minHeight = height;
31559 if(this.rotate == 90 || this.rotate == 270){
31560 this.minWidth = height;
31561 this.minHeight = width;
31566 width = Math.ceil(this.minWidth * height / this.minHeight);
31568 if(this.minWidth > this.minHeight){
31570 height = Math.ceil(this.minHeight * width / this.minWidth);
31573 this.thumbEl.setStyle({
31574 width : width + 'px',
31575 height : height + 'px'
31582 setThumbBoxPosition : function()
31584 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31585 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31587 this.thumbEl.setLeft(x);
31588 this.thumbEl.setTop(y);
31592 baseRotateLevel : function()
31594 this.baseRotate = 1;
31597 typeof(this.exif) != 'undefined' &&
31598 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31599 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31601 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31604 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31608 baseScaleLevel : function()
31612 if(this.isDocument){
31614 if(this.baseRotate == 6 || this.baseRotate == 8){
31616 height = this.thumbEl.getHeight();
31617 this.baseScale = height / this.imageEl.OriginWidth;
31619 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31620 width = this.thumbEl.getWidth();
31621 this.baseScale = width / this.imageEl.OriginHeight;
31627 height = this.thumbEl.getHeight();
31628 this.baseScale = height / this.imageEl.OriginHeight;
31630 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31631 width = this.thumbEl.getWidth();
31632 this.baseScale = width / this.imageEl.OriginWidth;
31638 if(this.baseRotate == 6 || this.baseRotate == 8){
31640 width = this.thumbEl.getHeight();
31641 this.baseScale = width / this.imageEl.OriginHeight;
31643 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31644 height = this.thumbEl.getWidth();
31645 this.baseScale = height / this.imageEl.OriginHeight;
31648 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31649 height = this.thumbEl.getWidth();
31650 this.baseScale = height / this.imageEl.OriginHeight;
31652 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31653 width = this.thumbEl.getHeight();
31654 this.baseScale = width / this.imageEl.OriginWidth;
31661 width = this.thumbEl.getWidth();
31662 this.baseScale = width / this.imageEl.OriginWidth;
31664 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31665 height = this.thumbEl.getHeight();
31666 this.baseScale = height / this.imageEl.OriginHeight;
31669 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31671 height = this.thumbEl.getHeight();
31672 this.baseScale = height / this.imageEl.OriginHeight;
31674 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31675 width = this.thumbEl.getWidth();
31676 this.baseScale = width / this.imageEl.OriginWidth;
31684 getScaleLevel : function()
31686 return this.baseScale * Math.pow(1.1, this.scale);
31689 onTouchStart : function(e)
31691 if(!this.canvasLoaded){
31692 this.beforeSelectFile(e);
31696 var touches = e.browserEvent.touches;
31702 if(touches.length == 1){
31703 this.onMouseDown(e);
31707 if(touches.length != 2){
31713 for(var i = 0, finger; finger = touches[i]; i++){
31714 coords.push(finger.pageX, finger.pageY);
31717 var x = Math.pow(coords[0] - coords[2], 2);
31718 var y = Math.pow(coords[1] - coords[3], 2);
31720 this.startDistance = Math.sqrt(x + y);
31722 this.startScale = this.scale;
31724 this.pinching = true;
31725 this.dragable = false;
31729 onTouchMove : function(e)
31731 if(!this.pinching && !this.dragable){
31735 var touches = e.browserEvent.touches;
31742 this.onMouseMove(e);
31748 for(var i = 0, finger; finger = touches[i]; i++){
31749 coords.push(finger.pageX, finger.pageY);
31752 var x = Math.pow(coords[0] - coords[2], 2);
31753 var y = Math.pow(coords[1] - coords[3], 2);
31755 this.endDistance = Math.sqrt(x + y);
31757 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31759 if(!this.zoomable()){
31760 this.scale = this.startScale;
31768 onTouchEnd : function(e)
31770 this.pinching = false;
31771 this.dragable = false;
31775 process : function(file, crop)
31778 this.maskEl.mask(this.loadingText);
31781 this.xhr = new XMLHttpRequest();
31783 file.xhr = this.xhr;
31785 this.xhr.open(this.method, this.url, true);
31788 "Accept": "application/json",
31789 "Cache-Control": "no-cache",
31790 "X-Requested-With": "XMLHttpRequest"
31793 for (var headerName in headers) {
31794 var headerValue = headers[headerName];
31796 this.xhr.setRequestHeader(headerName, headerValue);
31802 this.xhr.onload = function()
31804 _this.xhrOnLoad(_this.xhr);
31807 this.xhr.onerror = function()
31809 _this.xhrOnError(_this.xhr);
31812 var formData = new FormData();
31814 formData.append('returnHTML', 'NO');
31817 formData.append('crop', crop);
31820 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31821 formData.append(this.paramName, file, file.name);
31824 if(typeof(file.filename) != 'undefined'){
31825 formData.append('filename', file.filename);
31828 if(typeof(file.mimetype) != 'undefined'){
31829 formData.append('mimetype', file.mimetype);
31832 if(this.fireEvent('arrange', this, formData) != false){
31833 this.xhr.send(formData);
31837 xhrOnLoad : function(xhr)
31840 this.maskEl.unmask();
31843 if (xhr.readyState !== 4) {
31844 this.fireEvent('exception', this, xhr);
31848 var response = Roo.decode(xhr.responseText);
31850 if(!response.success){
31851 this.fireEvent('exception', this, xhr);
31855 var response = Roo.decode(xhr.responseText);
31857 this.fireEvent('upload', this, response);
31861 xhrOnError : function()
31864 this.maskEl.unmask();
31867 Roo.log('xhr on error');
31869 var response = Roo.decode(xhr.responseText);
31875 prepare : function(file)
31878 this.maskEl.mask(this.loadingText);
31884 if(typeof(file) === 'string'){
31885 this.loadCanvas(file);
31889 if(!file || !this.urlAPI){
31894 this.cropType = file.type;
31898 if(this.fireEvent('prepare', this, this.file) != false){
31900 var reader = new FileReader();
31902 reader.onload = function (e) {
31903 if (e.target.error) {
31904 Roo.log(e.target.error);
31908 var buffer = e.target.result,
31909 dataView = new DataView(buffer),
31911 maxOffset = dataView.byteLength - 4,
31915 if (dataView.getUint16(0) === 0xffd8) {
31916 while (offset < maxOffset) {
31917 markerBytes = dataView.getUint16(offset);
31919 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31920 markerLength = dataView.getUint16(offset + 2) + 2;
31921 if (offset + markerLength > dataView.byteLength) {
31922 Roo.log('Invalid meta data: Invalid segment size.');
31926 if(markerBytes == 0xffe1){
31927 _this.parseExifData(
31934 offset += markerLength;
31944 var url = _this.urlAPI.createObjectURL(_this.file);
31946 _this.loadCanvas(url);
31951 reader.readAsArrayBuffer(this.file);
31957 parseExifData : function(dataView, offset, length)
31959 var tiffOffset = offset + 10,
31963 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31964 // No Exif data, might be XMP data instead
31968 // Check for the ASCII code for "Exif" (0x45786966):
31969 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31970 // No Exif data, might be XMP data instead
31973 if (tiffOffset + 8 > dataView.byteLength) {
31974 Roo.log('Invalid Exif data: Invalid segment size.');
31977 // Check for the two null bytes:
31978 if (dataView.getUint16(offset + 8) !== 0x0000) {
31979 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31982 // Check the byte alignment:
31983 switch (dataView.getUint16(tiffOffset)) {
31985 littleEndian = true;
31988 littleEndian = false;
31991 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31994 // Check for the TIFF tag marker (0x002A):
31995 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31996 Roo.log('Invalid Exif data: Missing TIFF marker.');
31999 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32000 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32002 this.parseExifTags(
32005 tiffOffset + dirOffset,
32010 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32015 if (dirOffset + 6 > dataView.byteLength) {
32016 Roo.log('Invalid Exif data: Invalid directory offset.');
32019 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32020 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32021 if (dirEndOffset + 4 > dataView.byteLength) {
32022 Roo.log('Invalid Exif data: Invalid directory size.');
32025 for (i = 0; i < tagsNumber; i += 1) {
32029 dirOffset + 2 + 12 * i, // tag offset
32033 // Return the offset to the next directory:
32034 return dataView.getUint32(dirEndOffset, littleEndian);
32037 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32039 var tag = dataView.getUint16(offset, littleEndian);
32041 this.exif[tag] = this.getExifValue(
32045 dataView.getUint16(offset + 2, littleEndian), // tag type
32046 dataView.getUint32(offset + 4, littleEndian), // tag length
32051 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32053 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32062 Roo.log('Invalid Exif data: Invalid tag type.');
32066 tagSize = tagType.size * length;
32067 // Determine if the value is contained in the dataOffset bytes,
32068 // or if the value at the dataOffset is a pointer to the actual data:
32069 dataOffset = tagSize > 4 ?
32070 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32071 if (dataOffset + tagSize > dataView.byteLength) {
32072 Roo.log('Invalid Exif data: Invalid data offset.');
32075 if (length === 1) {
32076 return tagType.getValue(dataView, dataOffset, littleEndian);
32079 for (i = 0; i < length; i += 1) {
32080 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32083 if (tagType.ascii) {
32085 // Concatenate the chars:
32086 for (i = 0; i < values.length; i += 1) {
32088 // Ignore the terminating NULL byte(s):
32089 if (c === '\u0000') {
32101 Roo.apply(Roo.bootstrap.UploadCropbox, {
32103 'Orientation': 0x0112
32107 1: 0, //'top-left',
32109 3: 180, //'bottom-right',
32110 // 4: 'bottom-left',
32112 6: 90, //'right-top',
32113 // 7: 'right-bottom',
32114 8: 270 //'left-bottom'
32118 // byte, 8-bit unsigned int:
32120 getValue: function (dataView, dataOffset) {
32121 return dataView.getUint8(dataOffset);
32125 // ascii, 8-bit byte:
32127 getValue: function (dataView, dataOffset) {
32128 return String.fromCharCode(dataView.getUint8(dataOffset));
32133 // short, 16 bit int:
32135 getValue: function (dataView, dataOffset, littleEndian) {
32136 return dataView.getUint16(dataOffset, littleEndian);
32140 // long, 32 bit int:
32142 getValue: function (dataView, dataOffset, littleEndian) {
32143 return dataView.getUint32(dataOffset, littleEndian);
32147 // rational = two long values, first is numerator, second is denominator:
32149 getValue: function (dataView, dataOffset, littleEndian) {
32150 return dataView.getUint32(dataOffset, littleEndian) /
32151 dataView.getUint32(dataOffset + 4, littleEndian);
32155 // slong, 32 bit signed int:
32157 getValue: function (dataView, dataOffset, littleEndian) {
32158 return dataView.getInt32(dataOffset, littleEndian);
32162 // srational, two slongs, first is numerator, second is denominator:
32164 getValue: function (dataView, dataOffset, littleEndian) {
32165 return dataView.getInt32(dataOffset, littleEndian) /
32166 dataView.getInt32(dataOffset + 4, littleEndian);
32176 cls : 'btn-group roo-upload-cropbox-rotate-left',
32177 action : 'rotate-left',
32181 cls : 'btn btn-default',
32182 html : '<i class="fa fa-undo"></i>'
32188 cls : 'btn-group roo-upload-cropbox-picture',
32189 action : 'picture',
32193 cls : 'btn btn-default',
32194 html : '<i class="fa fa-picture-o"></i>'
32200 cls : 'btn-group roo-upload-cropbox-rotate-right',
32201 action : 'rotate-right',
32205 cls : 'btn btn-default',
32206 html : '<i class="fa fa-repeat"></i>'
32214 cls : 'btn-group roo-upload-cropbox-rotate-left',
32215 action : 'rotate-left',
32219 cls : 'btn btn-default',
32220 html : '<i class="fa fa-undo"></i>'
32226 cls : 'btn-group roo-upload-cropbox-download',
32227 action : 'download',
32231 cls : 'btn btn-default',
32232 html : '<i class="fa fa-download"></i>'
32238 cls : 'btn-group roo-upload-cropbox-crop',
32243 cls : 'btn btn-default',
32244 html : '<i class="fa fa-crop"></i>'
32250 cls : 'btn-group roo-upload-cropbox-trash',
32255 cls : 'btn btn-default',
32256 html : '<i class="fa fa-trash"></i>'
32262 cls : 'btn-group roo-upload-cropbox-rotate-right',
32263 action : 'rotate-right',
32267 cls : 'btn btn-default',
32268 html : '<i class="fa fa-repeat"></i>'
32276 cls : 'btn-group roo-upload-cropbox-rotate-left',
32277 action : 'rotate-left',
32281 cls : 'btn btn-default',
32282 html : '<i class="fa fa-undo"></i>'
32288 cls : 'btn-group roo-upload-cropbox-rotate-right',
32289 action : 'rotate-right',
32293 cls : 'btn btn-default',
32294 html : '<i class="fa fa-repeat"></i>'
32307 * @class Roo.bootstrap.DocumentManager
32308 * @extends Roo.bootstrap.Component
32309 * Bootstrap DocumentManager class
32310 * @cfg {String} paramName default 'imageUpload'
32311 * @cfg {String} toolTipName default 'filename'
32312 * @cfg {String} method default POST
32313 * @cfg {String} url action url
32314 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32315 * @cfg {Boolean} multiple multiple upload default true
32316 * @cfg {Number} thumbSize default 300
32317 * @cfg {String} fieldLabel
32318 * @cfg {Number} labelWidth default 4
32319 * @cfg {String} labelAlign (left|top) default left
32320 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32321 * @cfg {Number} labellg set the width of label (1-12)
32322 * @cfg {Number} labelmd set the width of label (1-12)
32323 * @cfg {Number} labelsm set the width of label (1-12)
32324 * @cfg {Number} labelxs set the width of label (1-12)
32327 * Create a new DocumentManager
32328 * @param {Object} config The config object
32331 Roo.bootstrap.DocumentManager = function(config){
32332 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32335 this.delegates = [];
32340 * Fire when initial the DocumentManager
32341 * @param {Roo.bootstrap.DocumentManager} this
32346 * inspect selected file
32347 * @param {Roo.bootstrap.DocumentManager} this
32348 * @param {File} file
32353 * Fire when xhr load exception
32354 * @param {Roo.bootstrap.DocumentManager} this
32355 * @param {XMLHttpRequest} xhr
32357 "exception" : true,
32359 * @event afterupload
32360 * Fire when xhr load exception
32361 * @param {Roo.bootstrap.DocumentManager} this
32362 * @param {XMLHttpRequest} xhr
32364 "afterupload" : true,
32367 * prepare the form data
32368 * @param {Roo.bootstrap.DocumentManager} this
32369 * @param {Object} formData
32374 * Fire when remove the file
32375 * @param {Roo.bootstrap.DocumentManager} this
32376 * @param {Object} file
32381 * Fire after refresh the file
32382 * @param {Roo.bootstrap.DocumentManager} this
32387 * Fire after click the image
32388 * @param {Roo.bootstrap.DocumentManager} this
32389 * @param {Object} file
32394 * Fire when upload a image and editable set to true
32395 * @param {Roo.bootstrap.DocumentManager} this
32396 * @param {Object} file
32400 * @event beforeselectfile
32401 * Fire before select file
32402 * @param {Roo.bootstrap.DocumentManager} this
32404 "beforeselectfile" : true,
32407 * Fire before process file
32408 * @param {Roo.bootstrap.DocumentManager} this
32409 * @param {Object} file
32413 * @event previewrendered
32414 * Fire when preview rendered
32415 * @param {Roo.bootstrap.DocumentManager} this
32416 * @param {Object} file
32418 "previewrendered" : true,
32421 "previewResize" : true
32426 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32435 paramName : 'imageUpload',
32436 toolTipName : 'filename',
32439 labelAlign : 'left',
32449 getAutoCreate : function()
32451 var managerWidget = {
32453 cls : 'roo-document-manager',
32457 cls : 'roo-document-manager-selector',
32462 cls : 'roo-document-manager-uploader',
32466 cls : 'roo-document-manager-upload-btn',
32467 html : '<i class="fa fa-plus"></i>'
32478 cls : 'column col-md-12',
32483 if(this.fieldLabel.length){
32488 cls : 'column col-md-12',
32489 html : this.fieldLabel
32493 cls : 'column col-md-12',
32498 if(this.labelAlign == 'left'){
32503 html : this.fieldLabel
32512 if(this.labelWidth > 12){
32513 content[0].style = "width: " + this.labelWidth + 'px';
32516 if(this.labelWidth < 13 && this.labelmd == 0){
32517 this.labelmd = this.labelWidth;
32520 if(this.labellg > 0){
32521 content[0].cls += ' col-lg-' + this.labellg;
32522 content[1].cls += ' col-lg-' + (12 - this.labellg);
32525 if(this.labelmd > 0){
32526 content[0].cls += ' col-md-' + this.labelmd;
32527 content[1].cls += ' col-md-' + (12 - this.labelmd);
32530 if(this.labelsm > 0){
32531 content[0].cls += ' col-sm-' + this.labelsm;
32532 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32535 if(this.labelxs > 0){
32536 content[0].cls += ' col-xs-' + this.labelxs;
32537 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32545 cls : 'row clearfix',
32553 initEvents : function()
32555 this.managerEl = this.el.select('.roo-document-manager', true).first();
32556 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32558 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32559 this.selectorEl.hide();
32562 this.selectorEl.attr('multiple', 'multiple');
32565 this.selectorEl.on('change', this.onFileSelected, this);
32567 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32568 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32570 this.uploader.on('click', this.onUploaderClick, this);
32572 this.renderProgressDialog();
32576 window.addEventListener("resize", function() { _this.refresh(); } );
32578 this.fireEvent('initial', this);
32581 renderProgressDialog : function()
32585 this.progressDialog = new Roo.bootstrap.Modal({
32586 cls : 'roo-document-manager-progress-dialog',
32587 allow_close : false,
32598 btnclick : function() {
32599 _this.uploadCancel();
32605 this.progressDialog.render(Roo.get(document.body));
32607 this.progress = new Roo.bootstrap.Progress({
32608 cls : 'roo-document-manager-progress',
32613 this.progress.render(this.progressDialog.getChildContainer());
32615 this.progressBar = new Roo.bootstrap.ProgressBar({
32616 cls : 'roo-document-manager-progress-bar',
32619 aria_valuemax : 12,
32623 this.progressBar.render(this.progress.getChildContainer());
32626 onUploaderClick : function(e)
32628 e.preventDefault();
32630 if(this.fireEvent('beforeselectfile', this) != false){
32631 this.selectorEl.dom.click();
32636 onFileSelected : function(e)
32638 e.preventDefault();
32640 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32644 Roo.each(this.selectorEl.dom.files, function(file){
32645 if(this.fireEvent('inspect', this, file) != false){
32646 this.files.push(file);
32656 this.selectorEl.dom.value = '';
32658 if(!this.files || !this.files.length){
32662 if(this.boxes > 0 && this.files.length > this.boxes){
32663 this.files = this.files.slice(0, this.boxes);
32666 this.uploader.show();
32668 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32669 this.uploader.hide();
32678 Roo.each(this.files, function(file){
32680 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32681 var f = this.renderPreview(file);
32686 if(file.type.indexOf('image') != -1){
32687 this.delegates.push(
32689 _this.process(file);
32690 }).createDelegate(this)
32698 _this.process(file);
32699 }).createDelegate(this)
32704 this.files = files;
32706 this.delegates = this.delegates.concat(docs);
32708 if(!this.delegates.length){
32713 this.progressBar.aria_valuemax = this.delegates.length;
32720 arrange : function()
32722 if(!this.delegates.length){
32723 this.progressDialog.hide();
32728 var delegate = this.delegates.shift();
32730 this.progressDialog.show();
32732 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32734 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32739 refresh : function()
32741 this.uploader.show();
32743 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32744 this.uploader.hide();
32747 Roo.isTouch ? this.closable(false) : this.closable(true);
32749 this.fireEvent('refresh', this);
32752 onRemove : function(e, el, o)
32754 e.preventDefault();
32756 this.fireEvent('remove', this, o);
32760 remove : function(o)
32764 Roo.each(this.files, function(file){
32765 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32774 this.files = files;
32781 Roo.each(this.files, function(file){
32786 file.target.remove();
32795 onClick : function(e, el, o)
32797 e.preventDefault();
32799 this.fireEvent('click', this, o);
32803 closable : function(closable)
32805 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32807 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32819 xhrOnLoad : function(xhr)
32821 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32825 if (xhr.readyState !== 4) {
32827 this.fireEvent('exception', this, xhr);
32831 var response = Roo.decode(xhr.responseText);
32833 if(!response.success){
32835 this.fireEvent('exception', this, xhr);
32839 var file = this.renderPreview(response.data);
32841 this.files.push(file);
32845 this.fireEvent('afterupload', this, xhr);
32849 xhrOnError : function(xhr)
32851 Roo.log('xhr on error');
32853 var response = Roo.decode(xhr.responseText);
32860 process : function(file)
32862 if(this.fireEvent('process', this, file) !== false){
32863 if(this.editable && file.type.indexOf('image') != -1){
32864 this.fireEvent('edit', this, file);
32868 this.uploadStart(file, false);
32875 uploadStart : function(file, crop)
32877 this.xhr = new XMLHttpRequest();
32879 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32884 file.xhr = this.xhr;
32886 this.managerEl.createChild({
32888 cls : 'roo-document-manager-loading',
32892 tooltip : file.name,
32893 cls : 'roo-document-manager-thumb',
32894 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32900 this.xhr.open(this.method, this.url, true);
32903 "Accept": "application/json",
32904 "Cache-Control": "no-cache",
32905 "X-Requested-With": "XMLHttpRequest"
32908 for (var headerName in headers) {
32909 var headerValue = headers[headerName];
32911 this.xhr.setRequestHeader(headerName, headerValue);
32917 this.xhr.onload = function()
32919 _this.xhrOnLoad(_this.xhr);
32922 this.xhr.onerror = function()
32924 _this.xhrOnError(_this.xhr);
32927 var formData = new FormData();
32929 formData.append('returnHTML', 'NO');
32932 formData.append('crop', crop);
32935 formData.append(this.paramName, file, file.name);
32942 if(this.fireEvent('prepare', this, formData, options) != false){
32944 if(options.manually){
32948 this.xhr.send(formData);
32952 this.uploadCancel();
32955 uploadCancel : function()
32961 this.delegates = [];
32963 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32970 renderPreview : function(file)
32972 if(typeof(file.target) != 'undefined' && file.target){
32976 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32978 var previewEl = this.managerEl.createChild({
32980 cls : 'roo-document-manager-preview',
32984 tooltip : file[this.toolTipName],
32985 cls : 'roo-document-manager-thumb',
32986 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32991 html : '<i class="fa fa-times-circle"></i>'
32996 var close = previewEl.select('button.close', true).first();
32998 close.on('click', this.onRemove, this, file);
33000 file.target = previewEl;
33002 var image = previewEl.select('img', true).first();
33006 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33008 image.on('click', this.onClick, this, file);
33010 this.fireEvent('previewrendered', this, file);
33016 onPreviewLoad : function(file, image)
33018 if(typeof(file.target) == 'undefined' || !file.target){
33022 var width = image.dom.naturalWidth || image.dom.width;
33023 var height = image.dom.naturalHeight || image.dom.height;
33025 if(!this.previewResize) {
33029 if(width > height){
33030 file.target.addClass('wide');
33034 file.target.addClass('tall');
33039 uploadFromSource : function(file, crop)
33041 this.xhr = new XMLHttpRequest();
33043 this.managerEl.createChild({
33045 cls : 'roo-document-manager-loading',
33049 tooltip : file.name,
33050 cls : 'roo-document-manager-thumb',
33051 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33057 this.xhr.open(this.method, this.url, true);
33060 "Accept": "application/json",
33061 "Cache-Control": "no-cache",
33062 "X-Requested-With": "XMLHttpRequest"
33065 for (var headerName in headers) {
33066 var headerValue = headers[headerName];
33068 this.xhr.setRequestHeader(headerName, headerValue);
33074 this.xhr.onload = function()
33076 _this.xhrOnLoad(_this.xhr);
33079 this.xhr.onerror = function()
33081 _this.xhrOnError(_this.xhr);
33084 var formData = new FormData();
33086 formData.append('returnHTML', 'NO');
33088 formData.append('crop', crop);
33090 if(typeof(file.filename) != 'undefined'){
33091 formData.append('filename', file.filename);
33094 if(typeof(file.mimetype) != 'undefined'){
33095 formData.append('mimetype', file.mimetype);
33100 if(this.fireEvent('prepare', this, formData) != false){
33101 this.xhr.send(formData);
33111 * @class Roo.bootstrap.DocumentViewer
33112 * @extends Roo.bootstrap.Component
33113 * Bootstrap DocumentViewer class
33114 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33115 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33118 * Create a new DocumentViewer
33119 * @param {Object} config The config object
33122 Roo.bootstrap.DocumentViewer = function(config){
33123 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33128 * Fire after initEvent
33129 * @param {Roo.bootstrap.DocumentViewer} this
33135 * @param {Roo.bootstrap.DocumentViewer} this
33140 * Fire after download button
33141 * @param {Roo.bootstrap.DocumentViewer} this
33146 * Fire after trash button
33147 * @param {Roo.bootstrap.DocumentViewer} this
33154 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33156 showDownload : true,
33160 getAutoCreate : function()
33164 cls : 'roo-document-viewer',
33168 cls : 'roo-document-viewer-body',
33172 cls : 'roo-document-viewer-thumb',
33176 cls : 'roo-document-viewer-image'
33184 cls : 'roo-document-viewer-footer',
33187 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33191 cls : 'btn-group roo-document-viewer-download',
33195 cls : 'btn btn-default',
33196 html : '<i class="fa fa-download"></i>'
33202 cls : 'btn-group roo-document-viewer-trash',
33206 cls : 'btn btn-default',
33207 html : '<i class="fa fa-trash"></i>'
33220 initEvents : function()
33222 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33223 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33225 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33226 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33228 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33229 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33231 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33232 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33234 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33235 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33237 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33238 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33240 this.bodyEl.on('click', this.onClick, this);
33241 this.downloadBtn.on('click', this.onDownload, this);
33242 this.trashBtn.on('click', this.onTrash, this);
33244 this.downloadBtn.hide();
33245 this.trashBtn.hide();
33247 if(this.showDownload){
33248 this.downloadBtn.show();
33251 if(this.showTrash){
33252 this.trashBtn.show();
33255 if(!this.showDownload && !this.showTrash) {
33256 this.footerEl.hide();
33261 initial : function()
33263 this.fireEvent('initial', this);
33267 onClick : function(e)
33269 e.preventDefault();
33271 this.fireEvent('click', this);
33274 onDownload : function(e)
33276 e.preventDefault();
33278 this.fireEvent('download', this);
33281 onTrash : function(e)
33283 e.preventDefault();
33285 this.fireEvent('trash', this);
33297 * @class Roo.bootstrap.NavProgressBar
33298 * @extends Roo.bootstrap.Component
33299 * Bootstrap NavProgressBar class
33302 * Create a new nav progress bar
33303 * @param {Object} config The config object
33306 Roo.bootstrap.NavProgressBar = function(config){
33307 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33309 this.bullets = this.bullets || [];
33311 // Roo.bootstrap.NavProgressBar.register(this);
33315 * Fires when the active item changes
33316 * @param {Roo.bootstrap.NavProgressBar} this
33317 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33318 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33325 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33330 getAutoCreate : function()
33332 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33336 cls : 'roo-navigation-bar-group',
33340 cls : 'roo-navigation-top-bar'
33344 cls : 'roo-navigation-bullets-bar',
33348 cls : 'roo-navigation-bar'
33355 cls : 'roo-navigation-bottom-bar'
33365 initEvents: function()
33370 onRender : function(ct, position)
33372 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33374 if(this.bullets.length){
33375 Roo.each(this.bullets, function(b){
33384 addItem : function(cfg)
33386 var item = new Roo.bootstrap.NavProgressItem(cfg);
33388 item.parentId = this.id;
33389 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33392 var top = new Roo.bootstrap.Element({
33394 cls : 'roo-navigation-bar-text'
33397 var bottom = new Roo.bootstrap.Element({
33399 cls : 'roo-navigation-bar-text'
33402 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33403 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33405 var topText = new Roo.bootstrap.Element({
33407 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33410 var bottomText = new Roo.bootstrap.Element({
33412 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33415 topText.onRender(top.el, null);
33416 bottomText.onRender(bottom.el, null);
33419 item.bottomEl = bottom;
33422 this.barItems.push(item);
33427 getActive : function()
33429 var active = false;
33431 Roo.each(this.barItems, function(v){
33433 if (!v.isActive()) {
33445 setActiveItem : function(item)
33449 Roo.each(this.barItems, function(v){
33450 if (v.rid == item.rid) {
33454 if (v.isActive()) {
33455 v.setActive(false);
33460 item.setActive(true);
33462 this.fireEvent('changed', this, item, prev);
33465 getBarItem: function(rid)
33469 Roo.each(this.barItems, function(e) {
33470 if (e.rid != rid) {
33481 indexOfItem : function(item)
33485 Roo.each(this.barItems, function(v, i){
33487 if (v.rid != item.rid) {
33498 setActiveNext : function()
33500 var i = this.indexOfItem(this.getActive());
33502 if (i > this.barItems.length) {
33506 this.setActiveItem(this.barItems[i+1]);
33509 setActivePrev : function()
33511 var i = this.indexOfItem(this.getActive());
33517 this.setActiveItem(this.barItems[i-1]);
33520 format : function()
33522 if(!this.barItems.length){
33526 var width = 100 / this.barItems.length;
33528 Roo.each(this.barItems, function(i){
33529 i.el.setStyle('width', width + '%');
33530 i.topEl.el.setStyle('width', width + '%');
33531 i.bottomEl.el.setStyle('width', width + '%');
33540 * Nav Progress Item
33545 * @class Roo.bootstrap.NavProgressItem
33546 * @extends Roo.bootstrap.Component
33547 * Bootstrap NavProgressItem class
33548 * @cfg {String} rid the reference id
33549 * @cfg {Boolean} active (true|false) Is item active default false
33550 * @cfg {Boolean} disabled (true|false) Is item active default false
33551 * @cfg {String} html
33552 * @cfg {String} position (top|bottom) text position default bottom
33553 * @cfg {String} icon show icon instead of number
33556 * Create a new NavProgressItem
33557 * @param {Object} config The config object
33559 Roo.bootstrap.NavProgressItem = function(config){
33560 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33565 * The raw click event for the entire grid.
33566 * @param {Roo.bootstrap.NavProgressItem} this
33567 * @param {Roo.EventObject} e
33574 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33580 position : 'bottom',
33583 getAutoCreate : function()
33585 var iconCls = 'roo-navigation-bar-item-icon';
33587 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33591 cls: 'roo-navigation-bar-item',
33601 cfg.cls += ' active';
33604 cfg.cls += ' disabled';
33610 disable : function()
33612 this.setDisabled(true);
33615 enable : function()
33617 this.setDisabled(false);
33620 initEvents: function()
33622 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33624 this.iconEl.on('click', this.onClick, this);
33627 onClick : function(e)
33629 e.preventDefault();
33635 if(this.fireEvent('click', this, e) === false){
33639 this.parent().setActiveItem(this);
33642 isActive: function ()
33644 return this.active;
33647 setActive : function(state)
33649 if(this.active == state){
33653 this.active = state;
33656 this.el.addClass('active');
33660 this.el.removeClass('active');
33665 setDisabled : function(state)
33667 if(this.disabled == state){
33671 this.disabled = state;
33674 this.el.addClass('disabled');
33678 this.el.removeClass('disabled');
33681 tooltipEl : function()
33683 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33696 * @class Roo.bootstrap.FieldLabel
33697 * @extends Roo.bootstrap.Component
33698 * Bootstrap FieldLabel class
33699 * @cfg {String} html contents of the element
33700 * @cfg {String} tag tag of the element default label
33701 * @cfg {String} cls class of the element
33702 * @cfg {String} target label target
33703 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33704 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33705 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33706 * @cfg {String} iconTooltip default "This field is required"
33707 * @cfg {String} indicatorpos (left|right) default left
33710 * Create a new FieldLabel
33711 * @param {Object} config The config object
33714 Roo.bootstrap.FieldLabel = function(config){
33715 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33720 * Fires after the field has been marked as invalid.
33721 * @param {Roo.form.FieldLabel} this
33722 * @param {String} msg The validation message
33727 * Fires after the field has been validated with no errors.
33728 * @param {Roo.form.FieldLabel} this
33734 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33741 invalidClass : 'has-warning',
33742 validClass : 'has-success',
33743 iconTooltip : 'This field is required',
33744 indicatorpos : 'left',
33746 getAutoCreate : function(){
33749 if (!this.allowBlank) {
33755 cls : 'roo-bootstrap-field-label ' + this.cls,
33760 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33761 tooltip : this.iconTooltip
33770 if(this.indicatorpos == 'right'){
33773 cls : 'roo-bootstrap-field-label ' + this.cls,
33782 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33783 tooltip : this.iconTooltip
33792 initEvents: function()
33794 Roo.bootstrap.Element.superclass.initEvents.call(this);
33796 this.indicator = this.indicatorEl();
33798 if(this.indicator){
33799 this.indicator.removeClass('visible');
33800 this.indicator.addClass('invisible');
33803 Roo.bootstrap.FieldLabel.register(this);
33806 indicatorEl : function()
33808 var indicator = this.el.select('i.roo-required-indicator',true).first();
33819 * Mark this field as valid
33821 markValid : function()
33823 if(this.indicator){
33824 this.indicator.removeClass('visible');
33825 this.indicator.addClass('invisible');
33827 if (Roo.bootstrap.version == 3) {
33828 this.el.removeClass(this.invalidClass);
33829 this.el.addClass(this.validClass);
33831 this.el.removeClass('is-invalid');
33832 this.el.addClass('is-valid');
33836 this.fireEvent('valid', this);
33840 * Mark this field as invalid
33841 * @param {String} msg The validation message
33843 markInvalid : function(msg)
33845 if(this.indicator){
33846 this.indicator.removeClass('invisible');
33847 this.indicator.addClass('visible');
33849 if (Roo.bootstrap.version == 3) {
33850 this.el.removeClass(this.validClass);
33851 this.el.addClass(this.invalidClass);
33853 this.el.removeClass('is-valid');
33854 this.el.addClass('is-invalid');
33858 this.fireEvent('invalid', this, msg);
33864 Roo.apply(Roo.bootstrap.FieldLabel, {
33869 * register a FieldLabel Group
33870 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33872 register : function(label)
33874 if(this.groups.hasOwnProperty(label.target)){
33878 this.groups[label.target] = label;
33882 * fetch a FieldLabel Group based on the target
33883 * @param {string} target
33884 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33886 get: function(target) {
33887 if (typeof(this.groups[target]) == 'undefined') {
33891 return this.groups[target] ;
33900 * page DateSplitField.
33906 * @class Roo.bootstrap.DateSplitField
33907 * @extends Roo.bootstrap.Component
33908 * Bootstrap DateSplitField class
33909 * @cfg {string} fieldLabel - the label associated
33910 * @cfg {Number} labelWidth set the width of label (0-12)
33911 * @cfg {String} labelAlign (top|left)
33912 * @cfg {Boolean} dayAllowBlank (true|false) default false
33913 * @cfg {Boolean} monthAllowBlank (true|false) default false
33914 * @cfg {Boolean} yearAllowBlank (true|false) default false
33915 * @cfg {string} dayPlaceholder
33916 * @cfg {string} monthPlaceholder
33917 * @cfg {string} yearPlaceholder
33918 * @cfg {string} dayFormat default 'd'
33919 * @cfg {string} monthFormat default 'm'
33920 * @cfg {string} yearFormat default 'Y'
33921 * @cfg {Number} labellg set the width of label (1-12)
33922 * @cfg {Number} labelmd set the width of label (1-12)
33923 * @cfg {Number} labelsm set the width of label (1-12)
33924 * @cfg {Number} labelxs set the width of label (1-12)
33928 * Create a new DateSplitField
33929 * @param {Object} config The config object
33932 Roo.bootstrap.DateSplitField = function(config){
33933 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33939 * getting the data of years
33940 * @param {Roo.bootstrap.DateSplitField} this
33941 * @param {Object} years
33946 * getting the data of days
33947 * @param {Roo.bootstrap.DateSplitField} this
33948 * @param {Object} days
33953 * Fires after the field has been marked as invalid.
33954 * @param {Roo.form.Field} this
33955 * @param {String} msg The validation message
33960 * Fires after the field has been validated with no errors.
33961 * @param {Roo.form.Field} this
33967 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33970 labelAlign : 'top',
33972 dayAllowBlank : false,
33973 monthAllowBlank : false,
33974 yearAllowBlank : false,
33975 dayPlaceholder : '',
33976 monthPlaceholder : '',
33977 yearPlaceholder : '',
33981 isFormField : true,
33987 getAutoCreate : function()
33991 cls : 'row roo-date-split-field-group',
33996 cls : 'form-hidden-field roo-date-split-field-group-value',
34002 var labelCls = 'col-md-12';
34003 var contentCls = 'col-md-4';
34005 if(this.fieldLabel){
34009 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34013 html : this.fieldLabel
34018 if(this.labelAlign == 'left'){
34020 if(this.labelWidth > 12){
34021 label.style = "width: " + this.labelWidth + 'px';
34024 if(this.labelWidth < 13 && this.labelmd == 0){
34025 this.labelmd = this.labelWidth;
34028 if(this.labellg > 0){
34029 labelCls = ' col-lg-' + this.labellg;
34030 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34033 if(this.labelmd > 0){
34034 labelCls = ' col-md-' + this.labelmd;
34035 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34038 if(this.labelsm > 0){
34039 labelCls = ' col-sm-' + this.labelsm;
34040 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34043 if(this.labelxs > 0){
34044 labelCls = ' col-xs-' + this.labelxs;
34045 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34049 label.cls += ' ' + labelCls;
34051 cfg.cn.push(label);
34054 Roo.each(['day', 'month', 'year'], function(t){
34057 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34064 inputEl: function ()
34066 return this.el.select('.roo-date-split-field-group-value', true).first();
34069 onRender : function(ct, position)
34073 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34075 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34077 this.dayField = new Roo.bootstrap.ComboBox({
34078 allowBlank : this.dayAllowBlank,
34079 alwaysQuery : true,
34080 displayField : 'value',
34083 forceSelection : true,
34085 placeholder : this.dayPlaceholder,
34086 selectOnFocus : true,
34087 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34088 triggerAction : 'all',
34090 valueField : 'value',
34091 store : new Roo.data.SimpleStore({
34092 data : (function() {
34094 _this.fireEvent('days', _this, days);
34097 fields : [ 'value' ]
34100 select : function (_self, record, index)
34102 _this.setValue(_this.getValue());
34107 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34109 this.monthField = new Roo.bootstrap.MonthField({
34110 after : '<i class=\"fa fa-calendar\"></i>',
34111 allowBlank : this.monthAllowBlank,
34112 placeholder : this.monthPlaceholder,
34115 render : function (_self)
34117 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34118 e.preventDefault();
34122 select : function (_self, oldvalue, newvalue)
34124 _this.setValue(_this.getValue());
34129 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34131 this.yearField = new Roo.bootstrap.ComboBox({
34132 allowBlank : this.yearAllowBlank,
34133 alwaysQuery : true,
34134 displayField : 'value',
34137 forceSelection : true,
34139 placeholder : this.yearPlaceholder,
34140 selectOnFocus : true,
34141 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34142 triggerAction : 'all',
34144 valueField : 'value',
34145 store : new Roo.data.SimpleStore({
34146 data : (function() {
34148 _this.fireEvent('years', _this, years);
34151 fields : [ 'value' ]
34154 select : function (_self, record, index)
34156 _this.setValue(_this.getValue());
34161 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34164 setValue : function(v, format)
34166 this.inputEl.dom.value = v;
34168 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34170 var d = Date.parseDate(v, f);
34177 this.setDay(d.format(this.dayFormat));
34178 this.setMonth(d.format(this.monthFormat));
34179 this.setYear(d.format(this.yearFormat));
34186 setDay : function(v)
34188 this.dayField.setValue(v);
34189 this.inputEl.dom.value = this.getValue();
34194 setMonth : function(v)
34196 this.monthField.setValue(v, true);
34197 this.inputEl.dom.value = this.getValue();
34202 setYear : function(v)
34204 this.yearField.setValue(v);
34205 this.inputEl.dom.value = this.getValue();
34210 getDay : function()
34212 return this.dayField.getValue();
34215 getMonth : function()
34217 return this.monthField.getValue();
34220 getYear : function()
34222 return this.yearField.getValue();
34225 getValue : function()
34227 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34229 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34239 this.inputEl.dom.value = '';
34244 validate : function()
34246 var d = this.dayField.validate();
34247 var m = this.monthField.validate();
34248 var y = this.yearField.validate();
34253 (!this.dayAllowBlank && !d) ||
34254 (!this.monthAllowBlank && !m) ||
34255 (!this.yearAllowBlank && !y)
34260 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34269 this.markInvalid();
34274 markValid : function()
34277 var label = this.el.select('label', true).first();
34278 var icon = this.el.select('i.fa-star', true).first();
34284 this.fireEvent('valid', this);
34288 * Mark this field as invalid
34289 * @param {String} msg The validation message
34291 markInvalid : function(msg)
34294 var label = this.el.select('label', true).first();
34295 var icon = this.el.select('i.fa-star', true).first();
34297 if(label && !icon){
34298 this.el.select('.roo-date-split-field-label', true).createChild({
34300 cls : 'text-danger fa fa-lg fa-star',
34301 tooltip : 'This field is required',
34302 style : 'margin-right:5px;'
34306 this.fireEvent('invalid', this, msg);
34309 clearInvalid : function()
34311 var label = this.el.select('label', true).first();
34312 var icon = this.el.select('i.fa-star', true).first();
34318 this.fireEvent('valid', this);
34321 getName: function()
34331 * http://masonry.desandro.com
34333 * The idea is to render all the bricks based on vertical width...
34335 * The original code extends 'outlayer' - we might need to use that....
34341 * @class Roo.bootstrap.LayoutMasonry
34342 * @extends Roo.bootstrap.Component
34343 * Bootstrap Layout Masonry class
34346 * Create a new Element
34347 * @param {Object} config The config object
34350 Roo.bootstrap.LayoutMasonry = function(config){
34352 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34356 Roo.bootstrap.LayoutMasonry.register(this);
34362 * Fire after layout the items
34363 * @param {Roo.bootstrap.LayoutMasonry} this
34364 * @param {Roo.EventObject} e
34371 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34374 * @cfg {Boolean} isLayoutInstant = no animation?
34376 isLayoutInstant : false, // needed?
34379 * @cfg {Number} boxWidth width of the columns
34384 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34389 * @cfg {Number} padWidth padding below box..
34394 * @cfg {Number} gutter gutter width..
34399 * @cfg {Number} maxCols maximum number of columns
34405 * @cfg {Boolean} isAutoInitial defalut true
34407 isAutoInitial : true,
34412 * @cfg {Boolean} isHorizontal defalut false
34414 isHorizontal : false,
34416 currentSize : null,
34422 bricks: null, //CompositeElement
34426 _isLayoutInited : false,
34428 // isAlternative : false, // only use for vertical layout...
34431 * @cfg {Number} alternativePadWidth padding below box..
34433 alternativePadWidth : 50,
34435 selectedBrick : [],
34437 getAutoCreate : function(){
34439 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34443 cls: 'blog-masonary-wrapper ' + this.cls,
34445 cls : 'mas-boxes masonary'
34452 getChildContainer: function( )
34454 if (this.boxesEl) {
34455 return this.boxesEl;
34458 this.boxesEl = this.el.select('.mas-boxes').first();
34460 return this.boxesEl;
34464 initEvents : function()
34468 if(this.isAutoInitial){
34469 Roo.log('hook children rendered');
34470 this.on('childrenrendered', function() {
34471 Roo.log('children rendered');
34477 initial : function()
34479 this.selectedBrick = [];
34481 this.currentSize = this.el.getBox(true);
34483 Roo.EventManager.onWindowResize(this.resize, this);
34485 if(!this.isAutoInitial){
34493 //this.layout.defer(500,this);
34497 resize : function()
34499 var cs = this.el.getBox(true);
34502 this.currentSize.width == cs.width &&
34503 this.currentSize.x == cs.x &&
34504 this.currentSize.height == cs.height &&
34505 this.currentSize.y == cs.y
34507 Roo.log("no change in with or X or Y");
34511 this.currentSize = cs;
34517 layout : function()
34519 this._resetLayout();
34521 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34523 this.layoutItems( isInstant );
34525 this._isLayoutInited = true;
34527 this.fireEvent('layout', this);
34531 _resetLayout : function()
34533 if(this.isHorizontal){
34534 this.horizontalMeasureColumns();
34538 this.verticalMeasureColumns();
34542 verticalMeasureColumns : function()
34544 this.getContainerWidth();
34546 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34547 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34551 var boxWidth = this.boxWidth + this.padWidth;
34553 if(this.containerWidth < this.boxWidth){
34554 boxWidth = this.containerWidth
34557 var containerWidth = this.containerWidth;
34559 var cols = Math.floor(containerWidth / boxWidth);
34561 this.cols = Math.max( cols, 1 );
34563 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34565 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34567 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34569 this.colWidth = boxWidth + avail - this.padWidth;
34571 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34572 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34575 horizontalMeasureColumns : function()
34577 this.getContainerWidth();
34579 var boxWidth = this.boxWidth;
34581 if(this.containerWidth < boxWidth){
34582 boxWidth = this.containerWidth;
34585 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34587 this.el.setHeight(boxWidth);
34591 getContainerWidth : function()
34593 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34596 layoutItems : function( isInstant )
34598 Roo.log(this.bricks);
34600 var items = Roo.apply([], this.bricks);
34602 if(this.isHorizontal){
34603 this._horizontalLayoutItems( items , isInstant );
34607 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34608 // this._verticalAlternativeLayoutItems( items , isInstant );
34612 this._verticalLayoutItems( items , isInstant );
34616 _verticalLayoutItems : function ( items , isInstant)
34618 if ( !items || !items.length ) {
34623 ['xs', 'xs', 'xs', 'tall'],
34624 ['xs', 'xs', 'tall'],
34625 ['xs', 'xs', 'sm'],
34626 ['xs', 'xs', 'xs'],
34632 ['sm', 'xs', 'xs'],
34636 ['tall', 'xs', 'xs', 'xs'],
34637 ['tall', 'xs', 'xs'],
34649 Roo.each(items, function(item, k){
34651 switch (item.size) {
34652 // these layouts take up a full box,
34663 boxes.push([item]);
34686 var filterPattern = function(box, length)
34694 var pattern = box.slice(0, length);
34698 Roo.each(pattern, function(i){
34699 format.push(i.size);
34702 Roo.each(standard, function(s){
34704 if(String(s) != String(format)){
34713 if(!match && length == 1){
34718 filterPattern(box, length - 1);
34722 queue.push(pattern);
34724 box = box.slice(length, box.length);
34726 filterPattern(box, 4);
34732 Roo.each(boxes, function(box, k){
34738 if(box.length == 1){
34743 filterPattern(box, 4);
34747 this._processVerticalLayoutQueue( queue, isInstant );
34751 // _verticalAlternativeLayoutItems : function( items , isInstant )
34753 // if ( !items || !items.length ) {
34757 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34761 _horizontalLayoutItems : function ( items , isInstant)
34763 if ( !items || !items.length || items.length < 3) {
34769 var eItems = items.slice(0, 3);
34771 items = items.slice(3, items.length);
34774 ['xs', 'xs', 'xs', 'wide'],
34775 ['xs', 'xs', 'wide'],
34776 ['xs', 'xs', 'sm'],
34777 ['xs', 'xs', 'xs'],
34783 ['sm', 'xs', 'xs'],
34787 ['wide', 'xs', 'xs', 'xs'],
34788 ['wide', 'xs', 'xs'],
34801 Roo.each(items, function(item, k){
34803 switch (item.size) {
34814 boxes.push([item]);
34838 var filterPattern = function(box, length)
34846 var pattern = box.slice(0, length);
34850 Roo.each(pattern, function(i){
34851 format.push(i.size);
34854 Roo.each(standard, function(s){
34856 if(String(s) != String(format)){
34865 if(!match && length == 1){
34870 filterPattern(box, length - 1);
34874 queue.push(pattern);
34876 box = box.slice(length, box.length);
34878 filterPattern(box, 4);
34884 Roo.each(boxes, function(box, k){
34890 if(box.length == 1){
34895 filterPattern(box, 4);
34902 var pos = this.el.getBox(true);
34906 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34908 var hit_end = false;
34910 Roo.each(queue, function(box){
34914 Roo.each(box, function(b){
34916 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34926 Roo.each(box, function(b){
34928 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34931 mx = Math.max(mx, b.x);
34935 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34939 Roo.each(box, function(b){
34941 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34955 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34958 /** Sets position of item in DOM
34959 * @param {Element} item
34960 * @param {Number} x - horizontal position
34961 * @param {Number} y - vertical position
34962 * @param {Boolean} isInstant - disables transitions
34964 _processVerticalLayoutQueue : function( queue, isInstant )
34966 var pos = this.el.getBox(true);
34971 for (var i = 0; i < this.cols; i++){
34975 Roo.each(queue, function(box, k){
34977 var col = k % this.cols;
34979 Roo.each(box, function(b,kk){
34981 b.el.position('absolute');
34983 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34984 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34986 if(b.size == 'md-left' || b.size == 'md-right'){
34987 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34988 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34991 b.el.setWidth(width);
34992 b.el.setHeight(height);
34994 b.el.select('iframe',true).setSize(width,height);
34998 for (var i = 0; i < this.cols; i++){
35000 if(maxY[i] < maxY[col]){
35005 col = Math.min(col, i);
35009 x = pos.x + col * (this.colWidth + this.padWidth);
35013 var positions = [];
35015 switch (box.length){
35017 positions = this.getVerticalOneBoxColPositions(x, y, box);
35020 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35023 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35026 positions = this.getVerticalFourBoxColPositions(x, y, box);
35032 Roo.each(box, function(b,kk){
35034 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35036 var sz = b.el.getSize();
35038 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35046 for (var i = 0; i < this.cols; i++){
35047 mY = Math.max(mY, maxY[i]);
35050 this.el.setHeight(mY - pos.y);
35054 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35056 // var pos = this.el.getBox(true);
35059 // var maxX = pos.right;
35061 // var maxHeight = 0;
35063 // Roo.each(items, function(item, k){
35067 // item.el.position('absolute');
35069 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35071 // item.el.setWidth(width);
35073 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35075 // item.el.setHeight(height);
35078 // item.el.setXY([x, y], isInstant ? false : true);
35080 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35083 // y = y + height + this.alternativePadWidth;
35085 // maxHeight = maxHeight + height + this.alternativePadWidth;
35089 // this.el.setHeight(maxHeight);
35093 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35095 var pos = this.el.getBox(true);
35100 var maxX = pos.right;
35102 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35104 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35106 Roo.each(queue, function(box, k){
35108 Roo.each(box, function(b, kk){
35110 b.el.position('absolute');
35112 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35113 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35115 if(b.size == 'md-left' || b.size == 'md-right'){
35116 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35117 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35120 b.el.setWidth(width);
35121 b.el.setHeight(height);
35129 var positions = [];
35131 switch (box.length){
35133 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35136 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35139 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35142 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35148 Roo.each(box, function(b,kk){
35150 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35152 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35160 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35162 Roo.each(eItems, function(b,k){
35164 b.size = (k == 0) ? 'sm' : 'xs';
35165 b.x = (k == 0) ? 2 : 1;
35166 b.y = (k == 0) ? 2 : 1;
35168 b.el.position('absolute');
35170 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35172 b.el.setWidth(width);
35174 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35176 b.el.setHeight(height);
35180 var positions = [];
35183 x : maxX - this.unitWidth * 2 - this.gutter,
35188 x : maxX - this.unitWidth,
35189 y : minY + (this.unitWidth + this.gutter) * 2
35193 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35197 Roo.each(eItems, function(b,k){
35199 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35205 getVerticalOneBoxColPositions : function(x, y, box)
35209 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35211 if(box[0].size == 'md-left'){
35215 if(box[0].size == 'md-right'){
35220 x : x + (this.unitWidth + this.gutter) * rand,
35227 getVerticalTwoBoxColPositions : function(x, y, box)
35231 if(box[0].size == 'xs'){
35235 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35239 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35253 x : x + (this.unitWidth + this.gutter) * 2,
35254 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35261 getVerticalThreeBoxColPositions : function(x, y, box)
35265 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35273 x : x + (this.unitWidth + this.gutter) * 1,
35278 x : x + (this.unitWidth + this.gutter) * 2,
35286 if(box[0].size == 'xs' && box[1].size == 'xs'){
35295 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35299 x : x + (this.unitWidth + this.gutter) * 1,
35313 x : x + (this.unitWidth + this.gutter) * 2,
35318 x : x + (this.unitWidth + this.gutter) * 2,
35319 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35326 getVerticalFourBoxColPositions : function(x, y, box)
35330 if(box[0].size == 'xs'){
35339 y : y + (this.unitHeight + this.gutter) * 1
35344 y : y + (this.unitHeight + this.gutter) * 2
35348 x : x + (this.unitWidth + this.gutter) * 1,
35362 x : x + (this.unitWidth + this.gutter) * 2,
35367 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35368 y : y + (this.unitHeight + this.gutter) * 1
35372 x : x + (this.unitWidth + this.gutter) * 2,
35373 y : y + (this.unitWidth + this.gutter) * 2
35380 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35384 if(box[0].size == 'md-left'){
35386 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35393 if(box[0].size == 'md-right'){
35395 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35396 y : minY + (this.unitWidth + this.gutter) * 1
35402 var rand = Math.floor(Math.random() * (4 - box[0].y));
35405 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35406 y : minY + (this.unitWidth + this.gutter) * rand
35413 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35417 if(box[0].size == 'xs'){
35420 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35425 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35426 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35434 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35439 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35440 y : minY + (this.unitWidth + this.gutter) * 2
35447 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35451 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35454 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35459 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35460 y : minY + (this.unitWidth + this.gutter) * 1
35464 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35465 y : minY + (this.unitWidth + this.gutter) * 2
35472 if(box[0].size == 'xs' && box[1].size == 'xs'){
35475 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35480 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35485 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35486 y : minY + (this.unitWidth + this.gutter) * 1
35494 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35499 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35500 y : minY + (this.unitWidth + this.gutter) * 2
35504 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35505 y : minY + (this.unitWidth + this.gutter) * 2
35512 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35516 if(box[0].size == 'xs'){
35519 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35524 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35529 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),
35534 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35535 y : minY + (this.unitWidth + this.gutter) * 1
35543 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35548 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35549 y : minY + (this.unitWidth + this.gutter) * 2
35553 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35554 y : minY + (this.unitWidth + this.gutter) * 2
35558 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),
35559 y : minY + (this.unitWidth + this.gutter) * 2
35567 * remove a Masonry Brick
35568 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35570 removeBrick : function(brick_id)
35576 for (var i = 0; i<this.bricks.length; i++) {
35577 if (this.bricks[i].id == brick_id) {
35578 this.bricks.splice(i,1);
35579 this.el.dom.removeChild(Roo.get(brick_id).dom);
35586 * adds a Masonry Brick
35587 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35589 addBrick : function(cfg)
35591 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35592 //this.register(cn);
35593 cn.parentId = this.id;
35594 cn.render(this.el);
35599 * register a Masonry Brick
35600 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35603 register : function(brick)
35605 this.bricks.push(brick);
35606 brick.masonryId = this.id;
35610 * clear all the Masonry Brick
35612 clearAll : function()
35615 //this.getChildContainer().dom.innerHTML = "";
35616 this.el.dom.innerHTML = '';
35619 getSelected : function()
35621 if (!this.selectedBrick) {
35625 return this.selectedBrick;
35629 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35633 * register a Masonry Layout
35634 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35637 register : function(layout)
35639 this.groups[layout.id] = layout;
35642 * fetch a Masonry Layout based on the masonry layout ID
35643 * @param {string} the masonry layout to add
35644 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35647 get: function(layout_id) {
35648 if (typeof(this.groups[layout_id]) == 'undefined') {
35651 return this.groups[layout_id] ;
35663 * http://masonry.desandro.com
35665 * The idea is to render all the bricks based on vertical width...
35667 * The original code extends 'outlayer' - we might need to use that....
35673 * @class Roo.bootstrap.LayoutMasonryAuto
35674 * @extends Roo.bootstrap.Component
35675 * Bootstrap Layout Masonry class
35678 * Create a new Element
35679 * @param {Object} config The config object
35682 Roo.bootstrap.LayoutMasonryAuto = function(config){
35683 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35686 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35689 * @cfg {Boolean} isFitWidth - resize the width..
35691 isFitWidth : false, // options..
35693 * @cfg {Boolean} isOriginLeft = left align?
35695 isOriginLeft : true,
35697 * @cfg {Boolean} isOriginTop = top align?
35699 isOriginTop : false,
35701 * @cfg {Boolean} isLayoutInstant = no animation?
35703 isLayoutInstant : false, // needed?
35705 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35707 isResizingContainer : true,
35709 * @cfg {Number} columnWidth width of the columns
35715 * @cfg {Number} maxCols maximum number of columns
35720 * @cfg {Number} padHeight padding below box..
35726 * @cfg {Boolean} isAutoInitial defalut true
35729 isAutoInitial : true,
35735 initialColumnWidth : 0,
35736 currentSize : null,
35738 colYs : null, // array.
35745 bricks: null, //CompositeElement
35746 cols : 0, // array?
35747 // element : null, // wrapped now this.el
35748 _isLayoutInited : null,
35751 getAutoCreate : function(){
35755 cls: 'blog-masonary-wrapper ' + this.cls,
35757 cls : 'mas-boxes masonary'
35764 getChildContainer: function( )
35766 if (this.boxesEl) {
35767 return this.boxesEl;
35770 this.boxesEl = this.el.select('.mas-boxes').first();
35772 return this.boxesEl;
35776 initEvents : function()
35780 if(this.isAutoInitial){
35781 Roo.log('hook children rendered');
35782 this.on('childrenrendered', function() {
35783 Roo.log('children rendered');
35790 initial : function()
35792 this.reloadItems();
35794 this.currentSize = this.el.getBox(true);
35796 /// was window resize... - let's see if this works..
35797 Roo.EventManager.onWindowResize(this.resize, this);
35799 if(!this.isAutoInitial){
35804 this.layout.defer(500,this);
35807 reloadItems: function()
35809 this.bricks = this.el.select('.masonry-brick', true);
35811 this.bricks.each(function(b) {
35812 //Roo.log(b.getSize());
35813 if (!b.attr('originalwidth')) {
35814 b.attr('originalwidth', b.getSize().width);
35819 Roo.log(this.bricks.elements.length);
35822 resize : function()
35825 var cs = this.el.getBox(true);
35827 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35828 Roo.log("no change in with or X");
35831 this.currentSize = cs;
35835 layout : function()
35838 this._resetLayout();
35839 //this._manageStamps();
35841 // don't animate first layout
35842 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35843 this.layoutItems( isInstant );
35845 // flag for initalized
35846 this._isLayoutInited = true;
35849 layoutItems : function( isInstant )
35851 //var items = this._getItemsForLayout( this.items );
35852 // original code supports filtering layout items.. we just ignore it..
35854 this._layoutItems( this.bricks , isInstant );
35856 this._postLayout();
35858 _layoutItems : function ( items , isInstant)
35860 //this.fireEvent( 'layout', this, items );
35863 if ( !items || !items.elements.length ) {
35864 // no items, emit event with empty array
35869 items.each(function(item) {
35870 Roo.log("layout item");
35872 // get x/y object from method
35873 var position = this._getItemLayoutPosition( item );
35875 position.item = item;
35876 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35877 queue.push( position );
35880 this._processLayoutQueue( queue );
35882 /** Sets position of item in DOM
35883 * @param {Element} item
35884 * @param {Number} x - horizontal position
35885 * @param {Number} y - vertical position
35886 * @param {Boolean} isInstant - disables transitions
35888 _processLayoutQueue : function( queue )
35890 for ( var i=0, len = queue.length; i < len; i++ ) {
35891 var obj = queue[i];
35892 obj.item.position('absolute');
35893 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35899 * Any logic you want to do after each layout,
35900 * i.e. size the container
35902 _postLayout : function()
35904 this.resizeContainer();
35907 resizeContainer : function()
35909 if ( !this.isResizingContainer ) {
35912 var size = this._getContainerSize();
35914 this.el.setSize(size.width,size.height);
35915 this.boxesEl.setSize(size.width,size.height);
35921 _resetLayout : function()
35923 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35924 this.colWidth = this.el.getWidth();
35925 //this.gutter = this.el.getWidth();
35927 this.measureColumns();
35933 this.colYs.push( 0 );
35939 measureColumns : function()
35941 this.getContainerWidth();
35942 // if columnWidth is 0, default to outerWidth of first item
35943 if ( !this.columnWidth ) {
35944 var firstItem = this.bricks.first();
35945 Roo.log(firstItem);
35946 this.columnWidth = this.containerWidth;
35947 if (firstItem && firstItem.attr('originalwidth') ) {
35948 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35950 // columnWidth fall back to item of first element
35951 Roo.log("set column width?");
35952 this.initialColumnWidth = this.columnWidth ;
35954 // if first elem has no width, default to size of container
35959 if (this.initialColumnWidth) {
35960 this.columnWidth = this.initialColumnWidth;
35965 // column width is fixed at the top - however if container width get's smaller we should
35968 // this bit calcs how man columns..
35970 var columnWidth = this.columnWidth += this.gutter;
35972 // calculate columns
35973 var containerWidth = this.containerWidth + this.gutter;
35975 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35976 // fix rounding errors, typically with gutters
35977 var excess = columnWidth - containerWidth % columnWidth;
35980 // if overshoot is less than a pixel, round up, otherwise floor it
35981 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35982 cols = Math[ mathMethod ]( cols );
35983 this.cols = Math.max( cols, 1 );
35984 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35986 // padding positioning..
35987 var totalColWidth = this.cols * this.columnWidth;
35988 var padavail = this.containerWidth - totalColWidth;
35989 // so for 2 columns - we need 3 'pads'
35991 var padNeeded = (1+this.cols) * this.padWidth;
35993 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35995 this.columnWidth += padExtra
35996 //this.padWidth = Math.floor(padavail / ( this.cols));
35998 // adjust colum width so that padding is fixed??
36000 // we have 3 columns ... total = width * 3
36001 // we have X left over... that should be used by
36003 //if (this.expandC) {
36011 getContainerWidth : function()
36013 /* // container is parent if fit width
36014 var container = this.isFitWidth ? this.element.parentNode : this.element;
36015 // check that this.size and size are there
36016 // IE8 triggers resize on body size change, so they might not be
36018 var size = getSize( container ); //FIXME
36019 this.containerWidth = size && size.innerWidth; //FIXME
36022 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36026 _getItemLayoutPosition : function( item ) // what is item?
36028 // we resize the item to our columnWidth..
36030 item.setWidth(this.columnWidth);
36031 item.autoBoxAdjust = false;
36033 var sz = item.getSize();
36035 // how many columns does this brick span
36036 var remainder = this.containerWidth % this.columnWidth;
36038 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36039 // round if off by 1 pixel, otherwise use ceil
36040 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36041 colSpan = Math.min( colSpan, this.cols );
36043 // normally this should be '1' as we dont' currently allow multi width columns..
36045 var colGroup = this._getColGroup( colSpan );
36046 // get the minimum Y value from the columns
36047 var minimumY = Math.min.apply( Math, colGroup );
36048 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36050 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36052 // position the brick
36054 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36055 y: this.currentSize.y + minimumY + this.padHeight
36059 // apply setHeight to necessary columns
36060 var setHeight = minimumY + sz.height + this.padHeight;
36061 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36063 var setSpan = this.cols + 1 - colGroup.length;
36064 for ( var i = 0; i < setSpan; i++ ) {
36065 this.colYs[ shortColIndex + i ] = setHeight ;
36072 * @param {Number} colSpan - number of columns the element spans
36073 * @returns {Array} colGroup
36075 _getColGroup : function( colSpan )
36077 if ( colSpan < 2 ) {
36078 // if brick spans only one column, use all the column Ys
36083 // how many different places could this brick fit horizontally
36084 var groupCount = this.cols + 1 - colSpan;
36085 // for each group potential horizontal position
36086 for ( var i = 0; i < groupCount; i++ ) {
36087 // make an array of colY values for that one group
36088 var groupColYs = this.colYs.slice( i, i + colSpan );
36089 // and get the max value of the array
36090 colGroup[i] = Math.max.apply( Math, groupColYs );
36095 _manageStamp : function( stamp )
36097 var stampSize = stamp.getSize();
36098 var offset = stamp.getBox();
36099 // get the columns that this stamp affects
36100 var firstX = this.isOriginLeft ? offset.x : offset.right;
36101 var lastX = firstX + stampSize.width;
36102 var firstCol = Math.floor( firstX / this.columnWidth );
36103 firstCol = Math.max( 0, firstCol );
36105 var lastCol = Math.floor( lastX / this.columnWidth );
36106 // lastCol should not go over if multiple of columnWidth #425
36107 lastCol -= lastX % this.columnWidth ? 0 : 1;
36108 lastCol = Math.min( this.cols - 1, lastCol );
36110 // set colYs to bottom of the stamp
36111 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36114 for ( var i = firstCol; i <= lastCol; i++ ) {
36115 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36120 _getContainerSize : function()
36122 this.maxY = Math.max.apply( Math, this.colYs );
36127 if ( this.isFitWidth ) {
36128 size.width = this._getContainerFitWidth();
36134 _getContainerFitWidth : function()
36136 var unusedCols = 0;
36137 // count unused columns
36140 if ( this.colYs[i] !== 0 ) {
36145 // fit container to columns that have been used
36146 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36149 needsResizeLayout : function()
36151 var previousWidth = this.containerWidth;
36152 this.getContainerWidth();
36153 return previousWidth !== this.containerWidth;
36168 * @class Roo.bootstrap.MasonryBrick
36169 * @extends Roo.bootstrap.Component
36170 * Bootstrap MasonryBrick class
36173 * Create a new MasonryBrick
36174 * @param {Object} config The config object
36177 Roo.bootstrap.MasonryBrick = function(config){
36179 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36181 Roo.bootstrap.MasonryBrick.register(this);
36187 * When a MasonryBrick is clcik
36188 * @param {Roo.bootstrap.MasonryBrick} this
36189 * @param {Roo.EventObject} e
36195 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36198 * @cfg {String} title
36202 * @cfg {String} html
36206 * @cfg {String} bgimage
36210 * @cfg {String} videourl
36214 * @cfg {String} cls
36218 * @cfg {String} href
36222 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36227 * @cfg {String} placetitle (center|bottom)
36232 * @cfg {Boolean} isFitContainer defalut true
36234 isFitContainer : true,
36237 * @cfg {Boolean} preventDefault defalut false
36239 preventDefault : false,
36242 * @cfg {Boolean} inverse defalut false
36244 maskInverse : false,
36246 getAutoCreate : function()
36248 if(!this.isFitContainer){
36249 return this.getSplitAutoCreate();
36252 var cls = 'masonry-brick masonry-brick-full';
36254 if(this.href.length){
36255 cls += ' masonry-brick-link';
36258 if(this.bgimage.length){
36259 cls += ' masonry-brick-image';
36262 if(this.maskInverse){
36263 cls += ' mask-inverse';
36266 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36267 cls += ' enable-mask';
36271 cls += ' masonry-' + this.size + '-brick';
36274 if(this.placetitle.length){
36276 switch (this.placetitle) {
36278 cls += ' masonry-center-title';
36281 cls += ' masonry-bottom-title';
36288 if(!this.html.length && !this.bgimage.length){
36289 cls += ' masonry-center-title';
36292 if(!this.html.length && this.bgimage.length){
36293 cls += ' masonry-bottom-title';
36298 cls += ' ' + this.cls;
36302 tag: (this.href.length) ? 'a' : 'div',
36307 cls: 'masonry-brick-mask'
36311 cls: 'masonry-brick-paragraph',
36317 if(this.href.length){
36318 cfg.href = this.href;
36321 var cn = cfg.cn[1].cn;
36323 if(this.title.length){
36326 cls: 'masonry-brick-title',
36331 if(this.html.length){
36334 cls: 'masonry-brick-text',
36339 if (!this.title.length && !this.html.length) {
36340 cfg.cn[1].cls += ' hide';
36343 if(this.bgimage.length){
36346 cls: 'masonry-brick-image-view',
36351 if(this.videourl.length){
36352 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36353 // youtube support only?
36356 cls: 'masonry-brick-image-view',
36359 allowfullscreen : true
36367 getSplitAutoCreate : function()
36369 var cls = 'masonry-brick masonry-brick-split';
36371 if(this.href.length){
36372 cls += ' masonry-brick-link';
36375 if(this.bgimage.length){
36376 cls += ' masonry-brick-image';
36380 cls += ' masonry-' + this.size + '-brick';
36383 switch (this.placetitle) {
36385 cls += ' masonry-center-title';
36388 cls += ' masonry-bottom-title';
36391 if(!this.bgimage.length){
36392 cls += ' masonry-center-title';
36395 if(this.bgimage.length){
36396 cls += ' masonry-bottom-title';
36402 cls += ' ' + this.cls;
36406 tag: (this.href.length) ? 'a' : 'div',
36411 cls: 'masonry-brick-split-head',
36415 cls: 'masonry-brick-paragraph',
36422 cls: 'masonry-brick-split-body',
36428 if(this.href.length){
36429 cfg.href = this.href;
36432 if(this.title.length){
36433 cfg.cn[0].cn[0].cn.push({
36435 cls: 'masonry-brick-title',
36440 if(this.html.length){
36441 cfg.cn[1].cn.push({
36443 cls: 'masonry-brick-text',
36448 if(this.bgimage.length){
36449 cfg.cn[0].cn.push({
36451 cls: 'masonry-brick-image-view',
36456 if(this.videourl.length){
36457 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36458 // youtube support only?
36459 cfg.cn[0].cn.cn.push({
36461 cls: 'masonry-brick-image-view',
36464 allowfullscreen : true
36471 initEvents: function()
36473 switch (this.size) {
36506 this.el.on('touchstart', this.onTouchStart, this);
36507 this.el.on('touchmove', this.onTouchMove, this);
36508 this.el.on('touchend', this.onTouchEnd, this);
36509 this.el.on('contextmenu', this.onContextMenu, this);
36511 this.el.on('mouseenter' ,this.enter, this);
36512 this.el.on('mouseleave', this.leave, this);
36513 this.el.on('click', this.onClick, this);
36516 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36517 this.parent().bricks.push(this);
36522 onClick: function(e, el)
36524 var time = this.endTimer - this.startTimer;
36525 // Roo.log(e.preventDefault());
36528 e.preventDefault();
36533 if(!this.preventDefault){
36537 e.preventDefault();
36539 if (this.activeClass != '') {
36540 this.selectBrick();
36543 this.fireEvent('click', this, e);
36546 enter: function(e, el)
36548 e.preventDefault();
36550 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36554 if(this.bgimage.length && this.html.length){
36555 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36559 leave: function(e, el)
36561 e.preventDefault();
36563 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36567 if(this.bgimage.length && this.html.length){
36568 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36572 onTouchStart: function(e, el)
36574 // e.preventDefault();
36576 this.touchmoved = false;
36578 if(!this.isFitContainer){
36582 if(!this.bgimage.length || !this.html.length){
36586 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36588 this.timer = new Date().getTime();
36592 onTouchMove: function(e, el)
36594 this.touchmoved = true;
36597 onContextMenu : function(e,el)
36599 e.preventDefault();
36600 e.stopPropagation();
36604 onTouchEnd: function(e, el)
36606 // e.preventDefault();
36608 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36615 if(!this.bgimage.length || !this.html.length){
36617 if(this.href.length){
36618 window.location.href = this.href;
36624 if(!this.isFitContainer){
36628 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36630 window.location.href = this.href;
36633 //selection on single brick only
36634 selectBrick : function() {
36636 if (!this.parentId) {
36640 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36641 var index = m.selectedBrick.indexOf(this.id);
36644 m.selectedBrick.splice(index,1);
36645 this.el.removeClass(this.activeClass);
36649 for(var i = 0; i < m.selectedBrick.length; i++) {
36650 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36651 b.el.removeClass(b.activeClass);
36654 m.selectedBrick = [];
36656 m.selectedBrick.push(this.id);
36657 this.el.addClass(this.activeClass);
36661 isSelected : function(){
36662 return this.el.hasClass(this.activeClass);
36667 Roo.apply(Roo.bootstrap.MasonryBrick, {
36670 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36672 * register a Masonry Brick
36673 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36676 register : function(brick)
36678 //this.groups[brick.id] = brick;
36679 this.groups.add(brick.id, brick);
36682 * fetch a masonry brick based on the masonry brick ID
36683 * @param {string} the masonry brick to add
36684 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36687 get: function(brick_id)
36689 // if (typeof(this.groups[brick_id]) == 'undefined') {
36692 // return this.groups[brick_id] ;
36694 if(this.groups.key(brick_id)) {
36695 return this.groups.key(brick_id);
36713 * @class Roo.bootstrap.Brick
36714 * @extends Roo.bootstrap.Component
36715 * Bootstrap Brick class
36718 * Create a new Brick
36719 * @param {Object} config The config object
36722 Roo.bootstrap.Brick = function(config){
36723 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36729 * When a Brick is click
36730 * @param {Roo.bootstrap.Brick} this
36731 * @param {Roo.EventObject} e
36737 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36740 * @cfg {String} title
36744 * @cfg {String} html
36748 * @cfg {String} bgimage
36752 * @cfg {String} cls
36756 * @cfg {String} href
36760 * @cfg {String} video
36764 * @cfg {Boolean} square
36768 getAutoCreate : function()
36770 var cls = 'roo-brick';
36772 if(this.href.length){
36773 cls += ' roo-brick-link';
36776 if(this.bgimage.length){
36777 cls += ' roo-brick-image';
36780 if(!this.html.length && !this.bgimage.length){
36781 cls += ' roo-brick-center-title';
36784 if(!this.html.length && this.bgimage.length){
36785 cls += ' roo-brick-bottom-title';
36789 cls += ' ' + this.cls;
36793 tag: (this.href.length) ? 'a' : 'div',
36798 cls: 'roo-brick-paragraph',
36804 if(this.href.length){
36805 cfg.href = this.href;
36808 var cn = cfg.cn[0].cn;
36810 if(this.title.length){
36813 cls: 'roo-brick-title',
36818 if(this.html.length){
36821 cls: 'roo-brick-text',
36828 if(this.bgimage.length){
36831 cls: 'roo-brick-image-view',
36839 initEvents: function()
36841 if(this.title.length || this.html.length){
36842 this.el.on('mouseenter' ,this.enter, this);
36843 this.el.on('mouseleave', this.leave, this);
36846 Roo.EventManager.onWindowResize(this.resize, this);
36848 if(this.bgimage.length){
36849 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36850 this.imageEl.on('load', this.onImageLoad, this);
36857 onImageLoad : function()
36862 resize : function()
36864 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36866 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36868 if(this.bgimage.length){
36869 var image = this.el.select('.roo-brick-image-view', true).first();
36871 image.setWidth(paragraph.getWidth());
36874 image.setHeight(paragraph.getWidth());
36877 this.el.setHeight(image.getHeight());
36878 paragraph.setHeight(image.getHeight());
36884 enter: function(e, el)
36886 e.preventDefault();
36888 if(this.bgimage.length){
36889 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36890 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36894 leave: function(e, el)
36896 e.preventDefault();
36898 if(this.bgimage.length){
36899 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36900 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36915 * @class Roo.bootstrap.NumberField
36916 * @extends Roo.bootstrap.Input
36917 * Bootstrap NumberField class
36923 * Create a new NumberField
36924 * @param {Object} config The config object
36927 Roo.bootstrap.NumberField = function(config){
36928 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36931 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36934 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36936 allowDecimals : true,
36938 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36940 decimalSeparator : ".",
36942 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36944 decimalPrecision : 2,
36946 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36948 allowNegative : true,
36951 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36955 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36957 minValue : Number.NEGATIVE_INFINITY,
36959 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36961 maxValue : Number.MAX_VALUE,
36963 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36965 minText : "The minimum value for this field is {0}",
36967 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36969 maxText : "The maximum value for this field is {0}",
36971 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36972 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36974 nanText : "{0} is not a valid number",
36976 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36978 thousandsDelimiter : false,
36980 * @cfg {String} valueAlign alignment of value
36982 valueAlign : "left",
36984 getAutoCreate : function()
36986 var hiddenInput = {
36990 cls: 'hidden-number-input'
36994 hiddenInput.name = this.name;
36999 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37001 this.name = hiddenInput.name;
37003 if(cfg.cn.length > 0) {
37004 cfg.cn.push(hiddenInput);
37011 initEvents : function()
37013 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37015 var allowed = "0123456789";
37017 if(this.allowDecimals){
37018 allowed += this.decimalSeparator;
37021 if(this.allowNegative){
37025 if(this.thousandsDelimiter) {
37029 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37031 var keyPress = function(e){
37033 var k = e.getKey();
37035 var c = e.getCharCode();
37038 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37039 allowed.indexOf(String.fromCharCode(c)) === -1
37045 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37049 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37054 this.el.on("keypress", keyPress, this);
37057 validateValue : function(value)
37060 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37064 var num = this.parseValue(value);
37067 this.markInvalid(String.format(this.nanText, value));
37071 if(num < this.minValue){
37072 this.markInvalid(String.format(this.minText, this.minValue));
37076 if(num > this.maxValue){
37077 this.markInvalid(String.format(this.maxText, this.maxValue));
37084 getValue : function()
37086 var v = this.hiddenEl().getValue();
37088 return this.fixPrecision(this.parseValue(v));
37091 parseValue : function(value)
37093 if(this.thousandsDelimiter) {
37095 r = new RegExp(",", "g");
37096 value = value.replace(r, "");
37099 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37100 return isNaN(value) ? '' : value;
37103 fixPrecision : function(value)
37105 if(this.thousandsDelimiter) {
37107 r = new RegExp(",", "g");
37108 value = value.replace(r, "");
37111 var nan = isNaN(value);
37113 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37114 return nan ? '' : value;
37116 return parseFloat(value).toFixed(this.decimalPrecision);
37119 setValue : function(v)
37121 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37127 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37129 this.inputEl().dom.value = (v == '') ? '' :
37130 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37132 if(!this.allowZero && v === '0') {
37133 this.hiddenEl().dom.value = '';
37134 this.inputEl().dom.value = '';
37141 decimalPrecisionFcn : function(v)
37143 return Math.floor(v);
37146 beforeBlur : function()
37148 var v = this.parseValue(this.getRawValue());
37150 if(v || v === 0 || v === ''){
37155 hiddenEl : function()
37157 return this.el.select('input.hidden-number-input',true).first();
37169 * @class Roo.bootstrap.DocumentSlider
37170 * @extends Roo.bootstrap.Component
37171 * Bootstrap DocumentSlider class
37174 * Create a new DocumentViewer
37175 * @param {Object} config The config object
37178 Roo.bootstrap.DocumentSlider = function(config){
37179 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37186 * Fire after initEvent
37187 * @param {Roo.bootstrap.DocumentSlider} this
37192 * Fire after update
37193 * @param {Roo.bootstrap.DocumentSlider} this
37199 * @param {Roo.bootstrap.DocumentSlider} this
37205 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37211 getAutoCreate : function()
37215 cls : 'roo-document-slider',
37219 cls : 'roo-document-slider-header',
37223 cls : 'roo-document-slider-header-title'
37229 cls : 'roo-document-slider-body',
37233 cls : 'roo-document-slider-prev',
37237 cls : 'fa fa-chevron-left'
37243 cls : 'roo-document-slider-thumb',
37247 cls : 'roo-document-slider-image'
37253 cls : 'roo-document-slider-next',
37257 cls : 'fa fa-chevron-right'
37269 initEvents : function()
37271 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37272 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37274 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37275 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37277 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37278 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37280 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37281 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37283 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37284 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37286 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37287 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37289 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37290 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37292 this.thumbEl.on('click', this.onClick, this);
37294 this.prevIndicator.on('click', this.prev, this);
37296 this.nextIndicator.on('click', this.next, this);
37300 initial : function()
37302 if(this.files.length){
37303 this.indicator = 1;
37307 this.fireEvent('initial', this);
37310 update : function()
37312 this.imageEl.attr('src', this.files[this.indicator - 1]);
37314 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37316 this.prevIndicator.show();
37318 if(this.indicator == 1){
37319 this.prevIndicator.hide();
37322 this.nextIndicator.show();
37324 if(this.indicator == this.files.length){
37325 this.nextIndicator.hide();
37328 this.thumbEl.scrollTo('top');
37330 this.fireEvent('update', this);
37333 onClick : function(e)
37335 e.preventDefault();
37337 this.fireEvent('click', this);
37342 e.preventDefault();
37344 this.indicator = Math.max(1, this.indicator - 1);
37351 e.preventDefault();
37353 this.indicator = Math.min(this.files.length, this.indicator + 1);
37367 * @class Roo.bootstrap.RadioSet
37368 * @extends Roo.bootstrap.Input
37369 * Bootstrap RadioSet class
37370 * @cfg {String} indicatorpos (left|right) default left
37371 * @cfg {Boolean} inline (true|false) inline the element (default true)
37372 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37374 * Create a new RadioSet
37375 * @param {Object} config The config object
37378 Roo.bootstrap.RadioSet = function(config){
37380 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37384 Roo.bootstrap.RadioSet.register(this);
37389 * Fires when the element is checked or unchecked.
37390 * @param {Roo.bootstrap.RadioSet} this This radio
37391 * @param {Roo.bootstrap.Radio} item The checked item
37396 * Fires when the element is click.
37397 * @param {Roo.bootstrap.RadioSet} this This radio set
37398 * @param {Roo.bootstrap.Radio} item The checked item
37399 * @param {Roo.EventObject} e The event object
37406 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37414 indicatorpos : 'left',
37416 getAutoCreate : function()
37420 cls : 'roo-radio-set-label',
37424 html : this.fieldLabel
37428 if (Roo.bootstrap.version == 3) {
37431 if(this.indicatorpos == 'left'){
37434 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37435 tooltip : 'This field is required'
37440 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37441 tooltip : 'This field is required'
37447 cls : 'roo-radio-set-items'
37450 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37452 if (align === 'left' && this.fieldLabel.length) {
37455 cls : "roo-radio-set-right",
37461 if(this.labelWidth > 12){
37462 label.style = "width: " + this.labelWidth + 'px';
37465 if(this.labelWidth < 13 && this.labelmd == 0){
37466 this.labelmd = this.labelWidth;
37469 if(this.labellg > 0){
37470 label.cls += ' col-lg-' + this.labellg;
37471 items.cls += ' col-lg-' + (12 - this.labellg);
37474 if(this.labelmd > 0){
37475 label.cls += ' col-md-' + this.labelmd;
37476 items.cls += ' col-md-' + (12 - this.labelmd);
37479 if(this.labelsm > 0){
37480 label.cls += ' col-sm-' + this.labelsm;
37481 items.cls += ' col-sm-' + (12 - this.labelsm);
37484 if(this.labelxs > 0){
37485 label.cls += ' col-xs-' + this.labelxs;
37486 items.cls += ' col-xs-' + (12 - this.labelxs);
37492 cls : 'roo-radio-set',
37496 cls : 'roo-radio-set-input',
37499 value : this.value ? this.value : ''
37506 if(this.weight.length){
37507 cfg.cls += ' roo-radio-' + this.weight;
37511 cfg.cls += ' roo-radio-set-inline';
37515 ['xs','sm','md','lg'].map(function(size){
37516 if (settings[size]) {
37517 cfg.cls += ' col-' + size + '-' + settings[size];
37525 initEvents : function()
37527 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37528 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37530 if(!this.fieldLabel.length){
37531 this.labelEl.hide();
37534 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37535 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37537 this.indicator = this.indicatorEl();
37539 if(this.indicator){
37540 this.indicator.addClass('invisible');
37543 this.originalValue = this.getValue();
37547 inputEl: function ()
37549 return this.el.select('.roo-radio-set-input', true).first();
37552 getChildContainer : function()
37554 return this.itemsEl;
37557 register : function(item)
37559 this.radioes.push(item);
37563 validate : function()
37565 if(this.getVisibilityEl().hasClass('hidden')){
37571 Roo.each(this.radioes, function(i){
37580 if(this.allowBlank) {
37584 if(this.disabled || valid){
37589 this.markInvalid();
37594 markValid : function()
37596 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37597 this.indicatorEl().removeClass('visible');
37598 this.indicatorEl().addClass('invisible');
37602 if (Roo.bootstrap.version == 3) {
37603 this.el.removeClass([this.invalidClass, this.validClass]);
37604 this.el.addClass(this.validClass);
37606 this.el.removeClass(['is-invalid','is-valid']);
37607 this.el.addClass(['is-valid']);
37609 this.fireEvent('valid', this);
37612 markInvalid : function(msg)
37614 if(this.allowBlank || this.disabled){
37618 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37619 this.indicatorEl().removeClass('invisible');
37620 this.indicatorEl().addClass('visible');
37622 if (Roo.bootstrap.version == 3) {
37623 this.el.removeClass([this.invalidClass, this.validClass]);
37624 this.el.addClass(this.invalidClass);
37626 this.el.removeClass(['is-invalid','is-valid']);
37627 this.el.addClass(['is-invalid']);
37630 this.fireEvent('invalid', this, msg);
37634 setValue : function(v, suppressEvent)
37636 if(this.value === v){
37643 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37646 Roo.each(this.radioes, function(i){
37648 i.el.removeClass('checked');
37651 Roo.each(this.radioes, function(i){
37653 if(i.value === v || i.value.toString() === v.toString()){
37655 i.el.addClass('checked');
37657 if(suppressEvent !== true){
37658 this.fireEvent('check', this, i);
37669 clearInvalid : function(){
37671 if(!this.el || this.preventMark){
37675 this.el.removeClass([this.invalidClass]);
37677 this.fireEvent('valid', this);
37682 Roo.apply(Roo.bootstrap.RadioSet, {
37686 register : function(set)
37688 this.groups[set.name] = set;
37691 get: function(name)
37693 if (typeof(this.groups[name]) == 'undefined') {
37697 return this.groups[name] ;
37703 * Ext JS Library 1.1.1
37704 * Copyright(c) 2006-2007, Ext JS, LLC.
37706 * Originally Released Under LGPL - original licence link has changed is not relivant.
37709 * <script type="text/javascript">
37714 * @class Roo.bootstrap.SplitBar
37715 * @extends Roo.util.Observable
37716 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37720 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37721 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37722 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37723 split.minSize = 100;
37724 split.maxSize = 600;
37725 split.animate = true;
37726 split.on('moved', splitterMoved);
37729 * Create a new SplitBar
37730 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37731 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37732 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37733 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37734 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37735 position of the SplitBar).
37737 Roo.bootstrap.SplitBar = function(cfg){
37742 // dragElement : elm
37743 // resizingElement: el,
37745 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37746 // placement : Roo.bootstrap.SplitBar.LEFT ,
37747 // existingProxy ???
37750 this.el = Roo.get(cfg.dragElement, true);
37751 this.el.dom.unselectable = "on";
37753 this.resizingEl = Roo.get(cfg.resizingElement, true);
37757 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37758 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37761 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37764 * The minimum size of the resizing element. (Defaults to 0)
37770 * The maximum size of the resizing element. (Defaults to 2000)
37773 this.maxSize = 2000;
37776 * Whether to animate the transition to the new size
37779 this.animate = false;
37782 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37785 this.useShim = false;
37790 if(!cfg.existingProxy){
37792 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37794 this.proxy = Roo.get(cfg.existingProxy).dom;
37797 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37800 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37803 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37806 this.dragSpecs = {};
37809 * @private The adapter to use to positon and resize elements
37811 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37812 this.adapter.init(this);
37814 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37816 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37817 this.el.addClass("roo-splitbar-h");
37820 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37821 this.el.addClass("roo-splitbar-v");
37827 * Fires when the splitter is moved (alias for {@link #event-moved})
37828 * @param {Roo.bootstrap.SplitBar} this
37829 * @param {Number} newSize the new width or height
37834 * Fires when the splitter is moved
37835 * @param {Roo.bootstrap.SplitBar} this
37836 * @param {Number} newSize the new width or height
37840 * @event beforeresize
37841 * Fires before the splitter is dragged
37842 * @param {Roo.bootstrap.SplitBar} this
37844 "beforeresize" : true,
37846 "beforeapply" : true
37849 Roo.util.Observable.call(this);
37852 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37853 onStartProxyDrag : function(x, y){
37854 this.fireEvent("beforeresize", this);
37856 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37858 o.enableDisplayMode("block");
37859 // all splitbars share the same overlay
37860 Roo.bootstrap.SplitBar.prototype.overlay = o;
37862 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37863 this.overlay.show();
37864 Roo.get(this.proxy).setDisplayed("block");
37865 var size = this.adapter.getElementSize(this);
37866 this.activeMinSize = this.getMinimumSize();;
37867 this.activeMaxSize = this.getMaximumSize();;
37868 var c1 = size - this.activeMinSize;
37869 var c2 = Math.max(this.activeMaxSize - size, 0);
37870 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37871 this.dd.resetConstraints();
37872 this.dd.setXConstraint(
37873 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37874 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37876 this.dd.setYConstraint(0, 0);
37878 this.dd.resetConstraints();
37879 this.dd.setXConstraint(0, 0);
37880 this.dd.setYConstraint(
37881 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37882 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37885 this.dragSpecs.startSize = size;
37886 this.dragSpecs.startPoint = [x, y];
37887 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37891 * @private Called after the drag operation by the DDProxy
37893 onEndProxyDrag : function(e){
37894 Roo.get(this.proxy).setDisplayed(false);
37895 var endPoint = Roo.lib.Event.getXY(e);
37897 this.overlay.hide();
37900 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37901 newSize = this.dragSpecs.startSize +
37902 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37903 endPoint[0] - this.dragSpecs.startPoint[0] :
37904 this.dragSpecs.startPoint[0] - endPoint[0]
37907 newSize = this.dragSpecs.startSize +
37908 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37909 endPoint[1] - this.dragSpecs.startPoint[1] :
37910 this.dragSpecs.startPoint[1] - endPoint[1]
37913 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37914 if(newSize != this.dragSpecs.startSize){
37915 if(this.fireEvent('beforeapply', this, newSize) !== false){
37916 this.adapter.setElementSize(this, newSize);
37917 this.fireEvent("moved", this, newSize);
37918 this.fireEvent("resize", this, newSize);
37924 * Get the adapter this SplitBar uses
37925 * @return The adapter object
37927 getAdapter : function(){
37928 return this.adapter;
37932 * Set the adapter this SplitBar uses
37933 * @param {Object} adapter A SplitBar adapter object
37935 setAdapter : function(adapter){
37936 this.adapter = adapter;
37937 this.adapter.init(this);
37941 * Gets the minimum size for the resizing element
37942 * @return {Number} The minimum size
37944 getMinimumSize : function(){
37945 return this.minSize;
37949 * Sets the minimum size for the resizing element
37950 * @param {Number} minSize The minimum size
37952 setMinimumSize : function(minSize){
37953 this.minSize = minSize;
37957 * Gets the maximum size for the resizing element
37958 * @return {Number} The maximum size
37960 getMaximumSize : function(){
37961 return this.maxSize;
37965 * Sets the maximum size for the resizing element
37966 * @param {Number} maxSize The maximum size
37968 setMaximumSize : function(maxSize){
37969 this.maxSize = maxSize;
37973 * Sets the initialize size for the resizing element
37974 * @param {Number} size The initial size
37976 setCurrentSize : function(size){
37977 var oldAnimate = this.animate;
37978 this.animate = false;
37979 this.adapter.setElementSize(this, size);
37980 this.animate = oldAnimate;
37984 * Destroy this splitbar.
37985 * @param {Boolean} removeEl True to remove the element
37987 destroy : function(removeEl){
37989 this.shim.remove();
37992 this.proxy.parentNode.removeChild(this.proxy);
38000 * @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.
38002 Roo.bootstrap.SplitBar.createProxy = function(dir){
38003 var proxy = new Roo.Element(document.createElement("div"));
38004 proxy.unselectable();
38005 var cls = 'roo-splitbar-proxy';
38006 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38007 document.body.appendChild(proxy.dom);
38012 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38013 * Default Adapter. It assumes the splitter and resizing element are not positioned
38014 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38016 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38019 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38020 // do nothing for now
38021 init : function(s){
38025 * Called before drag operations to get the current size of the resizing element.
38026 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38028 getElementSize : function(s){
38029 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38030 return s.resizingEl.getWidth();
38032 return s.resizingEl.getHeight();
38037 * Called after drag operations to set the size of the resizing element.
38038 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38039 * @param {Number} newSize The new size to set
38040 * @param {Function} onComplete A function to be invoked when resizing is complete
38042 setElementSize : function(s, newSize, onComplete){
38043 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38045 s.resizingEl.setWidth(newSize);
38047 onComplete(s, newSize);
38050 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38055 s.resizingEl.setHeight(newSize);
38057 onComplete(s, newSize);
38060 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38067 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38068 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38069 * Adapter that moves the splitter element to align with the resized sizing element.
38070 * Used with an absolute positioned SplitBar.
38071 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38072 * document.body, make sure you assign an id to the body element.
38074 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38075 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38076 this.container = Roo.get(container);
38079 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38080 init : function(s){
38081 this.basic.init(s);
38084 getElementSize : function(s){
38085 return this.basic.getElementSize(s);
38088 setElementSize : function(s, newSize, onComplete){
38089 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38092 moveSplitter : function(s){
38093 var yes = Roo.bootstrap.SplitBar;
38094 switch(s.placement){
38096 s.el.setX(s.resizingEl.getRight());
38099 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38102 s.el.setY(s.resizingEl.getBottom());
38105 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38112 * Orientation constant - Create a vertical SplitBar
38116 Roo.bootstrap.SplitBar.VERTICAL = 1;
38119 * Orientation constant - Create a horizontal SplitBar
38123 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38126 * Placement constant - The resizing element is to the left of the splitter element
38130 Roo.bootstrap.SplitBar.LEFT = 1;
38133 * Placement constant - The resizing element is to the right of the splitter element
38137 Roo.bootstrap.SplitBar.RIGHT = 2;
38140 * Placement constant - The resizing element is positioned above the splitter element
38144 Roo.bootstrap.SplitBar.TOP = 3;
38147 * Placement constant - The resizing element is positioned under splitter element
38151 Roo.bootstrap.SplitBar.BOTTOM = 4;
38152 Roo.namespace("Roo.bootstrap.layout");/*
38154 * Ext JS Library 1.1.1
38155 * Copyright(c) 2006-2007, Ext JS, LLC.
38157 * Originally Released Under LGPL - original licence link has changed is not relivant.
38160 * <script type="text/javascript">
38164 * @class Roo.bootstrap.layout.Manager
38165 * @extends Roo.bootstrap.Component
38166 * Base class for layout managers.
38168 Roo.bootstrap.layout.Manager = function(config)
38170 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38176 /** false to disable window resize monitoring @type Boolean */
38177 this.monitorWindowResize = true;
38182 * Fires when a layout is performed.
38183 * @param {Roo.LayoutManager} this
38187 * @event regionresized
38188 * Fires when the user resizes a region.
38189 * @param {Roo.LayoutRegion} region The resized region
38190 * @param {Number} newSize The new size (width for east/west, height for north/south)
38192 "regionresized" : true,
38194 * @event regioncollapsed
38195 * Fires when a region is collapsed.
38196 * @param {Roo.LayoutRegion} region The collapsed region
38198 "regioncollapsed" : true,
38200 * @event regionexpanded
38201 * Fires when a region is expanded.
38202 * @param {Roo.LayoutRegion} region The expanded region
38204 "regionexpanded" : true
38206 this.updating = false;
38209 this.el = Roo.get(config.el);
38215 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38220 monitorWindowResize : true,
38226 onRender : function(ct, position)
38229 this.el = Roo.get(ct);
38232 //this.fireEvent('render',this);
38236 initEvents: function()
38240 // ie scrollbar fix
38241 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38242 document.body.scroll = "no";
38243 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38244 this.el.position('relative');
38246 this.id = this.el.id;
38247 this.el.addClass("roo-layout-container");
38248 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38249 if(this.el.dom != document.body ) {
38250 this.el.on('resize', this.layout,this);
38251 this.el.on('show', this.layout,this);
38257 * Returns true if this layout is currently being updated
38258 * @return {Boolean}
38260 isUpdating : function(){
38261 return this.updating;
38265 * Suspend the LayoutManager from doing auto-layouts while
38266 * making multiple add or remove calls
38268 beginUpdate : function(){
38269 this.updating = true;
38273 * Restore auto-layouts and optionally disable the manager from performing a layout
38274 * @param {Boolean} noLayout true to disable a layout update
38276 endUpdate : function(noLayout){
38277 this.updating = false;
38283 layout: function(){
38287 onRegionResized : function(region, newSize){
38288 this.fireEvent("regionresized", region, newSize);
38292 onRegionCollapsed : function(region){
38293 this.fireEvent("regioncollapsed", region);
38296 onRegionExpanded : function(region){
38297 this.fireEvent("regionexpanded", region);
38301 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38302 * performs box-model adjustments.
38303 * @return {Object} The size as an object {width: (the width), height: (the height)}
38305 getViewSize : function()
38308 if(this.el.dom != document.body){
38309 size = this.el.getSize();
38311 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38313 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38314 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38319 * Returns the Element this layout is bound to.
38320 * @return {Roo.Element}
38322 getEl : function(){
38327 * Returns the specified region.
38328 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38329 * @return {Roo.LayoutRegion}
38331 getRegion : function(target){
38332 return this.regions[target.toLowerCase()];
38335 onWindowResize : function(){
38336 if(this.monitorWindowResize){
38343 * Ext JS Library 1.1.1
38344 * Copyright(c) 2006-2007, Ext JS, LLC.
38346 * Originally Released Under LGPL - original licence link has changed is not relivant.
38349 * <script type="text/javascript">
38352 * @class Roo.bootstrap.layout.Border
38353 * @extends Roo.bootstrap.layout.Manager
38354 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38355 * please see: examples/bootstrap/nested.html<br><br>
38357 <b>The container the layout is rendered into can be either the body element or any other element.
38358 If it is not the body element, the container needs to either be an absolute positioned element,
38359 or you will need to add "position:relative" to the css of the container. You will also need to specify
38360 the container size if it is not the body element.</b>
38363 * Create a new Border
38364 * @param {Object} config Configuration options
38366 Roo.bootstrap.layout.Border = function(config){
38367 config = config || {};
38368 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38372 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38373 if(config[region]){
38374 config[region].region = region;
38375 this.addRegion(config[region]);
38381 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38383 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38385 parent : false, // this might point to a 'nest' or a ???
38388 * Creates and adds a new region if it doesn't already exist.
38389 * @param {String} target The target region key (north, south, east, west or center).
38390 * @param {Object} config The regions config object
38391 * @return {BorderLayoutRegion} The new region
38393 addRegion : function(config)
38395 if(!this.regions[config.region]){
38396 var r = this.factory(config);
38397 this.bindRegion(r);
38399 return this.regions[config.region];
38403 bindRegion : function(r){
38404 this.regions[r.config.region] = r;
38406 r.on("visibilitychange", this.layout, this);
38407 r.on("paneladded", this.layout, this);
38408 r.on("panelremoved", this.layout, this);
38409 r.on("invalidated", this.layout, this);
38410 r.on("resized", this.onRegionResized, this);
38411 r.on("collapsed", this.onRegionCollapsed, this);
38412 r.on("expanded", this.onRegionExpanded, this);
38416 * Performs a layout update.
38418 layout : function()
38420 if(this.updating) {
38424 // render all the rebions if they have not been done alreayd?
38425 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38426 if(this.regions[region] && !this.regions[region].bodyEl){
38427 this.regions[region].onRender(this.el)
38431 var size = this.getViewSize();
38432 var w = size.width;
38433 var h = size.height;
38438 //var x = 0, y = 0;
38440 var rs = this.regions;
38441 var north = rs["north"];
38442 var south = rs["south"];
38443 var west = rs["west"];
38444 var east = rs["east"];
38445 var center = rs["center"];
38446 //if(this.hideOnLayout){ // not supported anymore
38447 //c.el.setStyle("display", "none");
38449 if(north && north.isVisible()){
38450 var b = north.getBox();
38451 var m = north.getMargins();
38452 b.width = w - (m.left+m.right);
38455 centerY = b.height + b.y + m.bottom;
38456 centerH -= centerY;
38457 north.updateBox(this.safeBox(b));
38459 if(south && south.isVisible()){
38460 var b = south.getBox();
38461 var m = south.getMargins();
38462 b.width = w - (m.left+m.right);
38464 var totalHeight = (b.height + m.top + m.bottom);
38465 b.y = h - totalHeight + m.top;
38466 centerH -= totalHeight;
38467 south.updateBox(this.safeBox(b));
38469 if(west && west.isVisible()){
38470 var b = west.getBox();
38471 var m = west.getMargins();
38472 b.height = centerH - (m.top+m.bottom);
38474 b.y = centerY + m.top;
38475 var totalWidth = (b.width + m.left + m.right);
38476 centerX += totalWidth;
38477 centerW -= totalWidth;
38478 west.updateBox(this.safeBox(b));
38480 if(east && east.isVisible()){
38481 var b = east.getBox();
38482 var m = east.getMargins();
38483 b.height = centerH - (m.top+m.bottom);
38484 var totalWidth = (b.width + m.left + m.right);
38485 b.x = w - totalWidth + m.left;
38486 b.y = centerY + m.top;
38487 centerW -= totalWidth;
38488 east.updateBox(this.safeBox(b));
38491 var m = center.getMargins();
38493 x: centerX + m.left,
38494 y: centerY + m.top,
38495 width: centerW - (m.left+m.right),
38496 height: centerH - (m.top+m.bottom)
38498 //if(this.hideOnLayout){
38499 //center.el.setStyle("display", "block");
38501 center.updateBox(this.safeBox(centerBox));
38504 this.fireEvent("layout", this);
38508 safeBox : function(box){
38509 box.width = Math.max(0, box.width);
38510 box.height = Math.max(0, box.height);
38515 * Adds a ContentPanel (or subclass) to this layout.
38516 * @param {String} target The target region key (north, south, east, west or center).
38517 * @param {Roo.ContentPanel} panel The panel to add
38518 * @return {Roo.ContentPanel} The added panel
38520 add : function(target, panel){
38522 target = target.toLowerCase();
38523 return this.regions[target].add(panel);
38527 * Remove a ContentPanel (or subclass) to this layout.
38528 * @param {String} target The target region key (north, south, east, west or center).
38529 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38530 * @return {Roo.ContentPanel} The removed panel
38532 remove : function(target, panel){
38533 target = target.toLowerCase();
38534 return this.regions[target].remove(panel);
38538 * Searches all regions for a panel with the specified id
38539 * @param {String} panelId
38540 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38542 findPanel : function(panelId){
38543 var rs = this.regions;
38544 for(var target in rs){
38545 if(typeof rs[target] != "function"){
38546 var p = rs[target].getPanel(panelId);
38556 * Searches all regions for a panel with the specified id and activates (shows) it.
38557 * @param {String/ContentPanel} panelId The panels id or the panel itself
38558 * @return {Roo.ContentPanel} The shown panel or null
38560 showPanel : function(panelId) {
38561 var rs = this.regions;
38562 for(var target in rs){
38563 var r = rs[target];
38564 if(typeof r != "function"){
38565 if(r.hasPanel(panelId)){
38566 return r.showPanel(panelId);
38574 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38575 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38578 restoreState : function(provider){
38580 provider = Roo.state.Manager;
38582 var sm = new Roo.LayoutStateManager();
38583 sm.init(this, provider);
38589 * Adds a xtype elements to the layout.
38593 xtype : 'ContentPanel',
38600 xtype : 'NestedLayoutPanel',
38606 items : [ ... list of content panels or nested layout panels.. ]
38610 * @param {Object} cfg Xtype definition of item to add.
38612 addxtype : function(cfg)
38614 // basically accepts a pannel...
38615 // can accept a layout region..!?!?
38616 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38619 // theory? children can only be panels??
38621 //if (!cfg.xtype.match(/Panel$/)) {
38626 if (typeof(cfg.region) == 'undefined') {
38627 Roo.log("Failed to add Panel, region was not set");
38631 var region = cfg.region;
38637 xitems = cfg.items;
38642 if ( region == 'center') {
38643 Roo.log("Center: " + cfg.title);
38649 case 'Content': // ContentPanel (el, cfg)
38650 case 'Scroll': // ContentPanel (el, cfg)
38652 cfg.autoCreate = cfg.autoCreate || true;
38653 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38655 // var el = this.el.createChild();
38656 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38659 this.add(region, ret);
38663 case 'TreePanel': // our new panel!
38664 cfg.el = this.el.createChild();
38665 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38666 this.add(region, ret);
38671 // create a new Layout (which is a Border Layout...
38673 var clayout = cfg.layout;
38674 clayout.el = this.el.createChild();
38675 clayout.items = clayout.items || [];
38679 // replace this exitems with the clayout ones..
38680 xitems = clayout.items;
38682 // force background off if it's in center...
38683 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38684 cfg.background = false;
38686 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38689 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38690 //console.log('adding nested layout panel ' + cfg.toSource());
38691 this.add(region, ret);
38692 nb = {}; /// find first...
38697 // needs grid and region
38699 //var el = this.getRegion(region).el.createChild();
38701 *var el = this.el.createChild();
38702 // create the grid first...
38703 cfg.grid.container = el;
38704 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38707 if (region == 'center' && this.active ) {
38708 cfg.background = false;
38711 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38713 this.add(region, ret);
38715 if (cfg.background) {
38716 // render grid on panel activation (if panel background)
38717 ret.on('activate', function(gp) {
38718 if (!gp.grid.rendered) {
38719 // gp.grid.render(el);
38723 // cfg.grid.render(el);
38729 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38730 // it was the old xcomponent building that caused this before.
38731 // espeically if border is the top element in the tree.
38741 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38743 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38744 this.add(region, ret);
38748 throw "Can not add '" + cfg.xtype + "' to Border";
38754 this.beginUpdate();
38758 Roo.each(xitems, function(i) {
38759 region = nb && i.region ? i.region : false;
38761 var add = ret.addxtype(i);
38764 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38765 if (!i.background) {
38766 abn[region] = nb[region] ;
38773 // make the last non-background panel active..
38774 //if (nb) { Roo.log(abn); }
38777 for(var r in abn) {
38778 region = this.getRegion(r);
38780 // tried using nb[r], but it does not work..
38782 region.showPanel(abn[r]);
38793 factory : function(cfg)
38796 var validRegions = Roo.bootstrap.layout.Border.regions;
38798 var target = cfg.region;
38801 var r = Roo.bootstrap.layout;
38805 return new r.North(cfg);
38807 return new r.South(cfg);
38809 return new r.East(cfg);
38811 return new r.West(cfg);
38813 return new r.Center(cfg);
38815 throw 'Layout region "'+target+'" not supported.';
38822 * Ext JS Library 1.1.1
38823 * Copyright(c) 2006-2007, Ext JS, LLC.
38825 * Originally Released Under LGPL - original licence link has changed is not relivant.
38828 * <script type="text/javascript">
38832 * @class Roo.bootstrap.layout.Basic
38833 * @extends Roo.util.Observable
38834 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38835 * and does not have a titlebar, tabs or any other features. All it does is size and position
38836 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38837 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38838 * @cfg {string} region the region that it inhabits..
38839 * @cfg {bool} skipConfig skip config?
38843 Roo.bootstrap.layout.Basic = function(config){
38845 this.mgr = config.mgr;
38847 this.position = config.region;
38849 var skipConfig = config.skipConfig;
38853 * @scope Roo.BasicLayoutRegion
38857 * @event beforeremove
38858 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38859 * @param {Roo.LayoutRegion} this
38860 * @param {Roo.ContentPanel} panel The panel
38861 * @param {Object} e The cancel event object
38863 "beforeremove" : true,
38865 * @event invalidated
38866 * Fires when the layout for this region is changed.
38867 * @param {Roo.LayoutRegion} this
38869 "invalidated" : true,
38871 * @event visibilitychange
38872 * Fires when this region is shown or hidden
38873 * @param {Roo.LayoutRegion} this
38874 * @param {Boolean} visibility true or false
38876 "visibilitychange" : true,
38878 * @event paneladded
38879 * Fires when a panel is added.
38880 * @param {Roo.LayoutRegion} this
38881 * @param {Roo.ContentPanel} panel The panel
38883 "paneladded" : true,
38885 * @event panelremoved
38886 * Fires when a panel is removed.
38887 * @param {Roo.LayoutRegion} this
38888 * @param {Roo.ContentPanel} panel The panel
38890 "panelremoved" : true,
38892 * @event beforecollapse
38893 * Fires when this region before collapse.
38894 * @param {Roo.LayoutRegion} this
38896 "beforecollapse" : true,
38899 * Fires when this region is collapsed.
38900 * @param {Roo.LayoutRegion} this
38902 "collapsed" : true,
38905 * Fires when this region is expanded.
38906 * @param {Roo.LayoutRegion} this
38911 * Fires when this region is slid into view.
38912 * @param {Roo.LayoutRegion} this
38914 "slideshow" : true,
38917 * Fires when this region slides out of view.
38918 * @param {Roo.LayoutRegion} this
38920 "slidehide" : true,
38922 * @event panelactivated
38923 * Fires when a panel is activated.
38924 * @param {Roo.LayoutRegion} this
38925 * @param {Roo.ContentPanel} panel The activated panel
38927 "panelactivated" : true,
38930 * Fires when the user resizes this region.
38931 * @param {Roo.LayoutRegion} this
38932 * @param {Number} newSize The new size (width for east/west, height for north/south)
38936 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38937 this.panels = new Roo.util.MixedCollection();
38938 this.panels.getKey = this.getPanelId.createDelegate(this);
38940 this.activePanel = null;
38941 // ensure listeners are added...
38943 if (config.listeners || config.events) {
38944 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38945 listeners : config.listeners || {},
38946 events : config.events || {}
38950 if(skipConfig !== true){
38951 this.applyConfig(config);
38955 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38957 getPanelId : function(p){
38961 applyConfig : function(config){
38962 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38963 this.config = config;
38968 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38969 * the width, for horizontal (north, south) the height.
38970 * @param {Number} newSize The new width or height
38972 resizeTo : function(newSize){
38973 var el = this.el ? this.el :
38974 (this.activePanel ? this.activePanel.getEl() : null);
38976 switch(this.position){
38979 el.setWidth(newSize);
38980 this.fireEvent("resized", this, newSize);
38984 el.setHeight(newSize);
38985 this.fireEvent("resized", this, newSize);
38991 getBox : function(){
38992 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38995 getMargins : function(){
38996 return this.margins;
38999 updateBox : function(box){
39001 var el = this.activePanel.getEl();
39002 el.dom.style.left = box.x + "px";
39003 el.dom.style.top = box.y + "px";
39004 this.activePanel.setSize(box.width, box.height);
39008 * Returns the container element for this region.
39009 * @return {Roo.Element}
39011 getEl : function(){
39012 return this.activePanel;
39016 * Returns true if this region is currently visible.
39017 * @return {Boolean}
39019 isVisible : function(){
39020 return this.activePanel ? true : false;
39023 setActivePanel : function(panel){
39024 panel = this.getPanel(panel);
39025 if(this.activePanel && this.activePanel != panel){
39026 this.activePanel.setActiveState(false);
39027 this.activePanel.getEl().setLeftTop(-10000,-10000);
39029 this.activePanel = panel;
39030 panel.setActiveState(true);
39032 panel.setSize(this.box.width, this.box.height);
39034 this.fireEvent("panelactivated", this, panel);
39035 this.fireEvent("invalidated");
39039 * Show the specified panel.
39040 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39041 * @return {Roo.ContentPanel} The shown panel or null
39043 showPanel : function(panel){
39044 panel = this.getPanel(panel);
39046 this.setActivePanel(panel);
39052 * Get the active panel for this region.
39053 * @return {Roo.ContentPanel} The active panel or null
39055 getActivePanel : function(){
39056 return this.activePanel;
39060 * Add the passed ContentPanel(s)
39061 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39062 * @return {Roo.ContentPanel} The panel added (if only one was added)
39064 add : function(panel){
39065 if(arguments.length > 1){
39066 for(var i = 0, len = arguments.length; i < len; i++) {
39067 this.add(arguments[i]);
39071 if(this.hasPanel(panel)){
39072 this.showPanel(panel);
39075 var el = panel.getEl();
39076 if(el.dom.parentNode != this.mgr.el.dom){
39077 this.mgr.el.dom.appendChild(el.dom);
39079 if(panel.setRegion){
39080 panel.setRegion(this);
39082 this.panels.add(panel);
39083 el.setStyle("position", "absolute");
39084 if(!panel.background){
39085 this.setActivePanel(panel);
39086 if(this.config.initialSize && this.panels.getCount()==1){
39087 this.resizeTo(this.config.initialSize);
39090 this.fireEvent("paneladded", this, panel);
39095 * Returns true if the panel is in this region.
39096 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39097 * @return {Boolean}
39099 hasPanel : function(panel){
39100 if(typeof panel == "object"){ // must be panel obj
39101 panel = panel.getId();
39103 return this.getPanel(panel) ? true : false;
39107 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39108 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39109 * @param {Boolean} preservePanel Overrides the config preservePanel option
39110 * @return {Roo.ContentPanel} The panel that was removed
39112 remove : function(panel, preservePanel){
39113 panel = this.getPanel(panel);
39118 this.fireEvent("beforeremove", this, panel, e);
39119 if(e.cancel === true){
39122 var panelId = panel.getId();
39123 this.panels.removeKey(panelId);
39128 * Returns the panel specified or null if it's not in this region.
39129 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39130 * @return {Roo.ContentPanel}
39132 getPanel : function(id){
39133 if(typeof id == "object"){ // must be panel obj
39136 return this.panels.get(id);
39140 * Returns this regions position (north/south/east/west/center).
39143 getPosition: function(){
39144 return this.position;
39148 * Ext JS Library 1.1.1
39149 * Copyright(c) 2006-2007, Ext JS, LLC.
39151 * Originally Released Under LGPL - original licence link has changed is not relivant.
39154 * <script type="text/javascript">
39158 * @class Roo.bootstrap.layout.Region
39159 * @extends Roo.bootstrap.layout.Basic
39160 * This class represents a region in a layout manager.
39162 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39163 * @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})
39164 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39165 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39166 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39167 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39168 * @cfg {String} title The title for the region (overrides panel titles)
39169 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39170 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39171 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39172 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39173 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39174 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39175 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39176 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39177 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39178 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39180 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39181 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39182 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39183 * @cfg {Number} width For East/West panels
39184 * @cfg {Number} height For North/South panels
39185 * @cfg {Boolean} split To show the splitter
39186 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39188 * @cfg {string} cls Extra CSS classes to add to region
39190 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39191 * @cfg {string} region the region that it inhabits..
39194 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39195 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39197 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39198 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39199 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39201 Roo.bootstrap.layout.Region = function(config)
39203 this.applyConfig(config);
39205 var mgr = config.mgr;
39206 var pos = config.region;
39207 config.skipConfig = true;
39208 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39211 this.onRender(mgr.el);
39214 this.visible = true;
39215 this.collapsed = false;
39216 this.unrendered_panels = [];
39219 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39221 position: '', // set by wrapper (eg. north/south etc..)
39222 unrendered_panels : null, // unrendered panels.
39224 tabPosition : false,
39226 mgr: false, // points to 'Border'
39229 createBody : function(){
39230 /** This region's body element
39231 * @type Roo.Element */
39232 this.bodyEl = this.el.createChild({
39234 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39238 onRender: function(ctr, pos)
39240 var dh = Roo.DomHelper;
39241 /** This region's container element
39242 * @type Roo.Element */
39243 this.el = dh.append(ctr.dom, {
39245 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39247 /** This region's title element
39248 * @type Roo.Element */
39250 this.titleEl = dh.append(this.el.dom, {
39252 unselectable: "on",
39253 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39255 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39256 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39260 this.titleEl.enableDisplayMode();
39261 /** This region's title text element
39262 * @type HTMLElement */
39263 this.titleTextEl = this.titleEl.dom.firstChild;
39264 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39266 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39267 this.closeBtn.enableDisplayMode();
39268 this.closeBtn.on("click", this.closeClicked, this);
39269 this.closeBtn.hide();
39271 this.createBody(this.config);
39272 if(this.config.hideWhenEmpty){
39274 this.on("paneladded", this.validateVisibility, this);
39275 this.on("panelremoved", this.validateVisibility, this);
39277 if(this.autoScroll){
39278 this.bodyEl.setStyle("overflow", "auto");
39280 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39282 //if(c.titlebar !== false){
39283 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39284 this.titleEl.hide();
39286 this.titleEl.show();
39287 if(this.config.title){
39288 this.titleTextEl.innerHTML = this.config.title;
39292 if(this.config.collapsed){
39293 this.collapse(true);
39295 if(this.config.hidden){
39299 if (this.unrendered_panels && this.unrendered_panels.length) {
39300 for (var i =0;i< this.unrendered_panels.length; i++) {
39301 this.add(this.unrendered_panels[i]);
39303 this.unrendered_panels = null;
39309 applyConfig : function(c)
39312 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39313 var dh = Roo.DomHelper;
39314 if(c.titlebar !== false){
39315 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39316 this.collapseBtn.on("click", this.collapse, this);
39317 this.collapseBtn.enableDisplayMode();
39319 if(c.showPin === true || this.showPin){
39320 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39321 this.stickBtn.enableDisplayMode();
39322 this.stickBtn.on("click", this.expand, this);
39323 this.stickBtn.hide();
39328 /** This region's collapsed element
39329 * @type Roo.Element */
39332 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39333 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39336 if(c.floatable !== false){
39337 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39338 this.collapsedEl.on("click", this.collapseClick, this);
39341 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39342 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39343 id: "message", unselectable: "on", style:{"float":"left"}});
39344 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39346 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39347 this.expandBtn.on("click", this.expand, this);
39351 if(this.collapseBtn){
39352 this.collapseBtn.setVisible(c.collapsible == true);
39355 this.cmargins = c.cmargins || this.cmargins ||
39356 (this.position == "west" || this.position == "east" ?
39357 {top: 0, left: 2, right:2, bottom: 0} :
39358 {top: 2, left: 0, right:0, bottom: 2});
39360 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39363 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39365 this.autoScroll = c.autoScroll || false;
39370 this.duration = c.duration || .30;
39371 this.slideDuration = c.slideDuration || .45;
39376 * Returns true if this region is currently visible.
39377 * @return {Boolean}
39379 isVisible : function(){
39380 return this.visible;
39384 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39385 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39387 //setCollapsedTitle : function(title){
39388 // title = title || " ";
39389 // if(this.collapsedTitleTextEl){
39390 // this.collapsedTitleTextEl.innerHTML = title;
39394 getBox : function(){
39396 // if(!this.collapsed){
39397 b = this.el.getBox(false, true);
39399 // b = this.collapsedEl.getBox(false, true);
39404 getMargins : function(){
39405 return this.margins;
39406 //return this.collapsed ? this.cmargins : this.margins;
39409 highlight : function(){
39410 this.el.addClass("x-layout-panel-dragover");
39413 unhighlight : function(){
39414 this.el.removeClass("x-layout-panel-dragover");
39417 updateBox : function(box)
39419 if (!this.bodyEl) {
39420 return; // not rendered yet..
39424 if(!this.collapsed){
39425 this.el.dom.style.left = box.x + "px";
39426 this.el.dom.style.top = box.y + "px";
39427 this.updateBody(box.width, box.height);
39429 this.collapsedEl.dom.style.left = box.x + "px";
39430 this.collapsedEl.dom.style.top = box.y + "px";
39431 this.collapsedEl.setSize(box.width, box.height);
39434 this.tabs.autoSizeTabs();
39438 updateBody : function(w, h)
39441 this.el.setWidth(w);
39442 w -= this.el.getBorderWidth("rl");
39443 if(this.config.adjustments){
39444 w += this.config.adjustments[0];
39447 if(h !== null && h > 0){
39448 this.el.setHeight(h);
39449 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39450 h -= this.el.getBorderWidth("tb");
39451 if(this.config.adjustments){
39452 h += this.config.adjustments[1];
39454 this.bodyEl.setHeight(h);
39456 h = this.tabs.syncHeight(h);
39459 if(this.panelSize){
39460 w = w !== null ? w : this.panelSize.width;
39461 h = h !== null ? h : this.panelSize.height;
39463 if(this.activePanel){
39464 var el = this.activePanel.getEl();
39465 w = w !== null ? w : el.getWidth();
39466 h = h !== null ? h : el.getHeight();
39467 this.panelSize = {width: w, height: h};
39468 this.activePanel.setSize(w, h);
39470 if(Roo.isIE && this.tabs){
39471 this.tabs.el.repaint();
39476 * Returns the container element for this region.
39477 * @return {Roo.Element}
39479 getEl : function(){
39484 * Hides this region.
39487 //if(!this.collapsed){
39488 this.el.dom.style.left = "-2000px";
39491 // this.collapsedEl.dom.style.left = "-2000px";
39492 // this.collapsedEl.hide();
39494 this.visible = false;
39495 this.fireEvent("visibilitychange", this, false);
39499 * Shows this region if it was previously hidden.
39502 //if(!this.collapsed){
39505 // this.collapsedEl.show();
39507 this.visible = true;
39508 this.fireEvent("visibilitychange", this, true);
39511 closeClicked : function(){
39512 if(this.activePanel){
39513 this.remove(this.activePanel);
39517 collapseClick : function(e){
39519 e.stopPropagation();
39522 e.stopPropagation();
39528 * Collapses this region.
39529 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39532 collapse : function(skipAnim, skipCheck = false){
39533 if(this.collapsed) {
39537 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39539 this.collapsed = true;
39541 this.split.el.hide();
39543 if(this.config.animate && skipAnim !== true){
39544 this.fireEvent("invalidated", this);
39545 this.animateCollapse();
39547 this.el.setLocation(-20000,-20000);
39549 this.collapsedEl.show();
39550 this.fireEvent("collapsed", this);
39551 this.fireEvent("invalidated", this);
39557 animateCollapse : function(){
39562 * Expands this region if it was previously collapsed.
39563 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39564 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39567 expand : function(e, skipAnim){
39569 e.stopPropagation();
39571 if(!this.collapsed || this.el.hasActiveFx()) {
39575 this.afterSlideIn();
39578 this.collapsed = false;
39579 if(this.config.animate && skipAnim !== true){
39580 this.animateExpand();
39584 this.split.el.show();
39586 this.collapsedEl.setLocation(-2000,-2000);
39587 this.collapsedEl.hide();
39588 this.fireEvent("invalidated", this);
39589 this.fireEvent("expanded", this);
39593 animateExpand : function(){
39597 initTabs : function()
39599 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39601 var ts = new Roo.bootstrap.panel.Tabs({
39602 el: this.bodyEl.dom,
39604 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39605 disableTooltips: this.config.disableTabTips,
39606 toolbar : this.config.toolbar
39609 if(this.config.hideTabs){
39610 ts.stripWrap.setDisplayed(false);
39613 ts.resizeTabs = this.config.resizeTabs === true;
39614 ts.minTabWidth = this.config.minTabWidth || 40;
39615 ts.maxTabWidth = this.config.maxTabWidth || 250;
39616 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39617 ts.monitorResize = false;
39618 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39619 ts.bodyEl.addClass('roo-layout-tabs-body');
39620 this.panels.each(this.initPanelAsTab, this);
39623 initPanelAsTab : function(panel){
39624 var ti = this.tabs.addTab(
39628 this.config.closeOnTab && panel.isClosable(),
39631 if(panel.tabTip !== undefined){
39632 ti.setTooltip(panel.tabTip);
39634 ti.on("activate", function(){
39635 this.setActivePanel(panel);
39638 if(this.config.closeOnTab){
39639 ti.on("beforeclose", function(t, e){
39641 this.remove(panel);
39645 panel.tabItem = ti;
39650 updatePanelTitle : function(panel, title)
39652 if(this.activePanel == panel){
39653 this.updateTitle(title);
39656 var ti = this.tabs.getTab(panel.getEl().id);
39658 if(panel.tabTip !== undefined){
39659 ti.setTooltip(panel.tabTip);
39664 updateTitle : function(title){
39665 if(this.titleTextEl && !this.config.title){
39666 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39670 setActivePanel : function(panel)
39672 panel = this.getPanel(panel);
39673 if(this.activePanel && this.activePanel != panel){
39674 if(this.activePanel.setActiveState(false) === false){
39678 this.activePanel = panel;
39679 panel.setActiveState(true);
39680 if(this.panelSize){
39681 panel.setSize(this.panelSize.width, this.panelSize.height);
39684 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39686 this.updateTitle(panel.getTitle());
39688 this.fireEvent("invalidated", this);
39690 this.fireEvent("panelactivated", this, panel);
39694 * Shows the specified panel.
39695 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39696 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39698 showPanel : function(panel)
39700 panel = this.getPanel(panel);
39703 var tab = this.tabs.getTab(panel.getEl().id);
39704 if(tab.isHidden()){
39705 this.tabs.unhideTab(tab.id);
39709 this.setActivePanel(panel);
39716 * Get the active panel for this region.
39717 * @return {Roo.ContentPanel} The active panel or null
39719 getActivePanel : function(){
39720 return this.activePanel;
39723 validateVisibility : function(){
39724 if(this.panels.getCount() < 1){
39725 this.updateTitle(" ");
39726 this.closeBtn.hide();
39729 if(!this.isVisible()){
39736 * Adds the passed ContentPanel(s) to this region.
39737 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39738 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39740 add : function(panel)
39742 if(arguments.length > 1){
39743 for(var i = 0, len = arguments.length; i < len; i++) {
39744 this.add(arguments[i]);
39749 // if we have not been rendered yet, then we can not really do much of this..
39750 if (!this.bodyEl) {
39751 this.unrendered_panels.push(panel);
39758 if(this.hasPanel(panel)){
39759 this.showPanel(panel);
39762 panel.setRegion(this);
39763 this.panels.add(panel);
39764 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39765 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39766 // and hide them... ???
39767 this.bodyEl.dom.appendChild(panel.getEl().dom);
39768 if(panel.background !== true){
39769 this.setActivePanel(panel);
39771 this.fireEvent("paneladded", this, panel);
39778 this.initPanelAsTab(panel);
39782 if(panel.background !== true){
39783 this.tabs.activate(panel.getEl().id);
39785 this.fireEvent("paneladded", this, panel);
39790 * Hides the tab for the specified panel.
39791 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39793 hidePanel : function(panel){
39794 if(this.tabs && (panel = this.getPanel(panel))){
39795 this.tabs.hideTab(panel.getEl().id);
39800 * Unhides the tab for a previously hidden panel.
39801 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39803 unhidePanel : function(panel){
39804 if(this.tabs && (panel = this.getPanel(panel))){
39805 this.tabs.unhideTab(panel.getEl().id);
39809 clearPanels : function(){
39810 while(this.panels.getCount() > 0){
39811 this.remove(this.panels.first());
39816 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39817 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39818 * @param {Boolean} preservePanel Overrides the config preservePanel option
39819 * @return {Roo.ContentPanel} The panel that was removed
39821 remove : function(panel, preservePanel)
39823 panel = this.getPanel(panel);
39828 this.fireEvent("beforeremove", this, panel, e);
39829 if(e.cancel === true){
39832 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39833 var panelId = panel.getId();
39834 this.panels.removeKey(panelId);
39836 document.body.appendChild(panel.getEl().dom);
39839 this.tabs.removeTab(panel.getEl().id);
39840 }else if (!preservePanel){
39841 this.bodyEl.dom.removeChild(panel.getEl().dom);
39843 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39844 var p = this.panels.first();
39845 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39846 tempEl.appendChild(p.getEl().dom);
39847 this.bodyEl.update("");
39848 this.bodyEl.dom.appendChild(p.getEl().dom);
39850 this.updateTitle(p.getTitle());
39852 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39853 this.setActivePanel(p);
39855 panel.setRegion(null);
39856 if(this.activePanel == panel){
39857 this.activePanel = null;
39859 if(this.config.autoDestroy !== false && preservePanel !== true){
39860 try{panel.destroy();}catch(e){}
39862 this.fireEvent("panelremoved", this, panel);
39867 * Returns the TabPanel component used by this region
39868 * @return {Roo.TabPanel}
39870 getTabs : function(){
39874 createTool : function(parentEl, className){
39875 var btn = Roo.DomHelper.append(parentEl, {
39877 cls: "x-layout-tools-button",
39880 cls: "roo-layout-tools-button-inner " + className,
39884 btn.addClassOnOver("roo-layout-tools-button-over");
39889 * Ext JS Library 1.1.1
39890 * Copyright(c) 2006-2007, Ext JS, LLC.
39892 * Originally Released Under LGPL - original licence link has changed is not relivant.
39895 * <script type="text/javascript">
39901 * @class Roo.SplitLayoutRegion
39902 * @extends Roo.LayoutRegion
39903 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39905 Roo.bootstrap.layout.Split = function(config){
39906 this.cursor = config.cursor;
39907 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39910 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39912 splitTip : "Drag to resize.",
39913 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39914 useSplitTips : false,
39916 applyConfig : function(config){
39917 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39920 onRender : function(ctr,pos) {
39922 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39923 if(!this.config.split){
39928 var splitEl = Roo.DomHelper.append(ctr.dom, {
39930 id: this.el.id + "-split",
39931 cls: "roo-layout-split roo-layout-split-"+this.position,
39934 /** The SplitBar for this region
39935 * @type Roo.SplitBar */
39936 // does not exist yet...
39937 Roo.log([this.position, this.orientation]);
39939 this.split = new Roo.bootstrap.SplitBar({
39940 dragElement : splitEl,
39941 resizingElement: this.el,
39942 orientation : this.orientation
39945 this.split.on("moved", this.onSplitMove, this);
39946 this.split.useShim = this.config.useShim === true;
39947 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39948 if(this.useSplitTips){
39949 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39951 //if(config.collapsible){
39952 // this.split.el.on("dblclick", this.collapse, this);
39955 if(typeof this.config.minSize != "undefined"){
39956 this.split.minSize = this.config.minSize;
39958 if(typeof this.config.maxSize != "undefined"){
39959 this.split.maxSize = this.config.maxSize;
39961 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39962 this.hideSplitter();
39967 getHMaxSize : function(){
39968 var cmax = this.config.maxSize || 10000;
39969 var center = this.mgr.getRegion("center");
39970 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39973 getVMaxSize : function(){
39974 var cmax = this.config.maxSize || 10000;
39975 var center = this.mgr.getRegion("center");
39976 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39979 onSplitMove : function(split, newSize){
39980 this.fireEvent("resized", this, newSize);
39984 * Returns the {@link Roo.SplitBar} for this region.
39985 * @return {Roo.SplitBar}
39987 getSplitBar : function(){
39992 this.hideSplitter();
39993 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39996 hideSplitter : function(){
39998 this.split.el.setLocation(-2000,-2000);
39999 this.split.el.hide();
40005 this.split.el.show();
40007 Roo.bootstrap.layout.Split.superclass.show.call(this);
40010 beforeSlide: function(){
40011 if(Roo.isGecko){// firefox overflow auto bug workaround
40012 this.bodyEl.clip();
40014 this.tabs.bodyEl.clip();
40016 if(this.activePanel){
40017 this.activePanel.getEl().clip();
40019 if(this.activePanel.beforeSlide){
40020 this.activePanel.beforeSlide();
40026 afterSlide : function(){
40027 if(Roo.isGecko){// firefox overflow auto bug workaround
40028 this.bodyEl.unclip();
40030 this.tabs.bodyEl.unclip();
40032 if(this.activePanel){
40033 this.activePanel.getEl().unclip();
40034 if(this.activePanel.afterSlide){
40035 this.activePanel.afterSlide();
40041 initAutoHide : function(){
40042 if(this.autoHide !== false){
40043 if(!this.autoHideHd){
40044 var st = new Roo.util.DelayedTask(this.slideIn, this);
40045 this.autoHideHd = {
40046 "mouseout": function(e){
40047 if(!e.within(this.el, true)){
40051 "mouseover" : function(e){
40057 this.el.on(this.autoHideHd);
40061 clearAutoHide : function(){
40062 if(this.autoHide !== false){
40063 this.el.un("mouseout", this.autoHideHd.mouseout);
40064 this.el.un("mouseover", this.autoHideHd.mouseover);
40068 clearMonitor : function(){
40069 Roo.get(document).un("click", this.slideInIf, this);
40072 // these names are backwards but not changed for compat
40073 slideOut : function(){
40074 if(this.isSlid || this.el.hasActiveFx()){
40077 this.isSlid = true;
40078 if(this.collapseBtn){
40079 this.collapseBtn.hide();
40081 this.closeBtnState = this.closeBtn.getStyle('display');
40082 this.closeBtn.hide();
40084 this.stickBtn.show();
40087 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40088 this.beforeSlide();
40089 this.el.setStyle("z-index", 10001);
40090 this.el.slideIn(this.getSlideAnchor(), {
40091 callback: function(){
40093 this.initAutoHide();
40094 Roo.get(document).on("click", this.slideInIf, this);
40095 this.fireEvent("slideshow", this);
40102 afterSlideIn : function(){
40103 this.clearAutoHide();
40104 this.isSlid = false;
40105 this.clearMonitor();
40106 this.el.setStyle("z-index", "");
40107 if(this.collapseBtn){
40108 this.collapseBtn.show();
40110 this.closeBtn.setStyle('display', this.closeBtnState);
40112 this.stickBtn.hide();
40114 this.fireEvent("slidehide", this);
40117 slideIn : function(cb){
40118 if(!this.isSlid || this.el.hasActiveFx()){
40122 this.isSlid = false;
40123 this.beforeSlide();
40124 this.el.slideOut(this.getSlideAnchor(), {
40125 callback: function(){
40126 this.el.setLeftTop(-10000, -10000);
40128 this.afterSlideIn();
40136 slideInIf : function(e){
40137 if(!e.within(this.el)){
40142 animateCollapse : function(){
40143 this.beforeSlide();
40144 this.el.setStyle("z-index", 20000);
40145 var anchor = this.getSlideAnchor();
40146 this.el.slideOut(anchor, {
40147 callback : function(){
40148 this.el.setStyle("z-index", "");
40149 this.collapsedEl.slideIn(anchor, {duration:.3});
40151 this.el.setLocation(-10000,-10000);
40153 this.fireEvent("collapsed", this);
40160 animateExpand : function(){
40161 this.beforeSlide();
40162 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40163 this.el.setStyle("z-index", 20000);
40164 this.collapsedEl.hide({
40167 this.el.slideIn(this.getSlideAnchor(), {
40168 callback : function(){
40169 this.el.setStyle("z-index", "");
40172 this.split.el.show();
40174 this.fireEvent("invalidated", this);
40175 this.fireEvent("expanded", this);
40203 getAnchor : function(){
40204 return this.anchors[this.position];
40207 getCollapseAnchor : function(){
40208 return this.canchors[this.position];
40211 getSlideAnchor : function(){
40212 return this.sanchors[this.position];
40215 getAlignAdj : function(){
40216 var cm = this.cmargins;
40217 switch(this.position){
40233 getExpandAdj : function(){
40234 var c = this.collapsedEl, cm = this.cmargins;
40235 switch(this.position){
40237 return [-(cm.right+c.getWidth()+cm.left), 0];
40240 return [cm.right+c.getWidth()+cm.left, 0];
40243 return [0, -(cm.top+cm.bottom+c.getHeight())];
40246 return [0, cm.top+cm.bottom+c.getHeight()];
40252 * Ext JS Library 1.1.1
40253 * Copyright(c) 2006-2007, Ext JS, LLC.
40255 * Originally Released Under LGPL - original licence link has changed is not relivant.
40258 * <script type="text/javascript">
40261 * These classes are private internal classes
40263 Roo.bootstrap.layout.Center = function(config){
40264 config.region = "center";
40265 Roo.bootstrap.layout.Region.call(this, config);
40266 this.visible = true;
40267 this.minWidth = config.minWidth || 20;
40268 this.minHeight = config.minHeight || 20;
40271 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40273 // center panel can't be hidden
40277 // center panel can't be hidden
40280 getMinWidth: function(){
40281 return this.minWidth;
40284 getMinHeight: function(){
40285 return this.minHeight;
40299 Roo.bootstrap.layout.North = function(config)
40301 config.region = 'north';
40302 config.cursor = 'n-resize';
40304 Roo.bootstrap.layout.Split.call(this, config);
40308 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40309 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40310 this.split.el.addClass("roo-layout-split-v");
40312 //var size = config.initialSize || config.height;
40313 //if(this.el && typeof size != "undefined"){
40314 // this.el.setHeight(size);
40317 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40319 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40322 onRender : function(ctr, pos)
40324 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40325 var size = this.config.initialSize || this.config.height;
40326 if(this.el && typeof size != "undefined"){
40327 this.el.setHeight(size);
40332 getBox : function(){
40333 if(this.collapsed){
40334 return this.collapsedEl.getBox();
40336 var box = this.el.getBox();
40338 box.height += this.split.el.getHeight();
40343 updateBox : function(box){
40344 if(this.split && !this.collapsed){
40345 box.height -= this.split.el.getHeight();
40346 this.split.el.setLeft(box.x);
40347 this.split.el.setTop(box.y+box.height);
40348 this.split.el.setWidth(box.width);
40350 if(this.collapsed){
40351 this.updateBody(box.width, null);
40353 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40361 Roo.bootstrap.layout.South = function(config){
40362 config.region = 'south';
40363 config.cursor = 's-resize';
40364 Roo.bootstrap.layout.Split.call(this, config);
40366 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40367 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40368 this.split.el.addClass("roo-layout-split-v");
40373 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40374 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40376 onRender : function(ctr, pos)
40378 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40379 var size = this.config.initialSize || this.config.height;
40380 if(this.el && typeof size != "undefined"){
40381 this.el.setHeight(size);
40386 getBox : function(){
40387 if(this.collapsed){
40388 return this.collapsedEl.getBox();
40390 var box = this.el.getBox();
40392 var sh = this.split.el.getHeight();
40399 updateBox : function(box){
40400 if(this.split && !this.collapsed){
40401 var sh = this.split.el.getHeight();
40404 this.split.el.setLeft(box.x);
40405 this.split.el.setTop(box.y-sh);
40406 this.split.el.setWidth(box.width);
40408 if(this.collapsed){
40409 this.updateBody(box.width, null);
40411 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40415 Roo.bootstrap.layout.East = function(config){
40416 config.region = "east";
40417 config.cursor = "e-resize";
40418 Roo.bootstrap.layout.Split.call(this, config);
40420 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40421 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40422 this.split.el.addClass("roo-layout-split-h");
40426 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40427 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40429 onRender : function(ctr, pos)
40431 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40432 var size = this.config.initialSize || this.config.width;
40433 if(this.el && typeof size != "undefined"){
40434 this.el.setWidth(size);
40439 getBox : function(){
40440 if(this.collapsed){
40441 return this.collapsedEl.getBox();
40443 var box = this.el.getBox();
40445 var sw = this.split.el.getWidth();
40452 updateBox : function(box){
40453 if(this.split && !this.collapsed){
40454 var sw = this.split.el.getWidth();
40456 this.split.el.setLeft(box.x);
40457 this.split.el.setTop(box.y);
40458 this.split.el.setHeight(box.height);
40461 if(this.collapsed){
40462 this.updateBody(null, box.height);
40464 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40468 Roo.bootstrap.layout.West = function(config){
40469 config.region = "west";
40470 config.cursor = "w-resize";
40472 Roo.bootstrap.layout.Split.call(this, config);
40474 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40475 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40476 this.split.el.addClass("roo-layout-split-h");
40480 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40481 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40483 onRender: function(ctr, pos)
40485 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40486 var size = this.config.initialSize || this.config.width;
40487 if(typeof size != "undefined"){
40488 this.el.setWidth(size);
40492 getBox : function(){
40493 if(this.collapsed){
40494 return this.collapsedEl.getBox();
40496 var box = this.el.getBox();
40497 if (box.width == 0) {
40498 box.width = this.config.width; // kludge?
40501 box.width += this.split.el.getWidth();
40506 updateBox : function(box){
40507 if(this.split && !this.collapsed){
40508 var sw = this.split.el.getWidth();
40510 this.split.el.setLeft(box.x+box.width);
40511 this.split.el.setTop(box.y);
40512 this.split.el.setHeight(box.height);
40514 if(this.collapsed){
40515 this.updateBody(null, box.height);
40517 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40519 });Roo.namespace("Roo.bootstrap.panel");/*
40521 * Ext JS Library 1.1.1
40522 * Copyright(c) 2006-2007, Ext JS, LLC.
40524 * Originally Released Under LGPL - original licence link has changed is not relivant.
40527 * <script type="text/javascript">
40530 * @class Roo.ContentPanel
40531 * @extends Roo.util.Observable
40532 * A basic ContentPanel element.
40533 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40534 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40535 * @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
40536 * @cfg {Boolean} closable True if the panel can be closed/removed
40537 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40538 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40539 * @cfg {Toolbar} toolbar A toolbar for this panel
40540 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40541 * @cfg {String} title The title for this panel
40542 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40543 * @cfg {String} url Calls {@link #setUrl} with this value
40544 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40545 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40546 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40547 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40548 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40549 * @cfg {Boolean} badges render the badges
40550 * @cfg {String} cls extra classes to use
40551 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40554 * Create a new ContentPanel.
40555 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40556 * @param {String/Object} config A string to set only the title or a config object
40557 * @param {String} content (optional) Set the HTML content for this panel
40558 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40560 Roo.bootstrap.panel.Content = function( config){
40562 this.tpl = config.tpl || false;
40564 var el = config.el;
40565 var content = config.content;
40567 if(config.autoCreate){ // xtype is available if this is called from factory
40570 this.el = Roo.get(el);
40571 if(!this.el && config && config.autoCreate){
40572 if(typeof config.autoCreate == "object"){
40573 if(!config.autoCreate.id){
40574 config.autoCreate.id = config.id||el;
40576 this.el = Roo.DomHelper.append(document.body,
40577 config.autoCreate, true);
40581 cls: (config.cls || '') +
40582 (config.background ? ' bg-' + config.background : '') +
40583 " roo-layout-inactive-content",
40586 if (config.iframe) {
40590 style : 'border: 0px',
40591 src : 'about:blank'
40597 elcfg.html = config.html;
40601 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40602 if (config.iframe) {
40603 this.iframeEl = this.el.select('iframe',true).first();
40608 this.closable = false;
40609 this.loaded = false;
40610 this.active = false;
40613 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40615 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40617 this.wrapEl = this.el; //this.el.wrap();
40619 if (config.toolbar.items) {
40620 ti = config.toolbar.items ;
40621 delete config.toolbar.items ;
40625 this.toolbar.render(this.wrapEl, 'before');
40626 for(var i =0;i < ti.length;i++) {
40627 // Roo.log(['add child', items[i]]);
40628 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40630 this.toolbar.items = nitems;
40631 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40632 delete config.toolbar;
40636 // xtype created footer. - not sure if will work as we normally have to render first..
40637 if (this.footer && !this.footer.el && this.footer.xtype) {
40638 if (!this.wrapEl) {
40639 this.wrapEl = this.el.wrap();
40642 this.footer.container = this.wrapEl.createChild();
40644 this.footer = Roo.factory(this.footer, Roo);
40649 if(typeof config == "string"){
40650 this.title = config;
40652 Roo.apply(this, config);
40656 this.resizeEl = Roo.get(this.resizeEl, true);
40658 this.resizeEl = this.el;
40660 // handle view.xtype
40668 * Fires when this panel is activated.
40669 * @param {Roo.ContentPanel} this
40673 * @event deactivate
40674 * Fires when this panel is activated.
40675 * @param {Roo.ContentPanel} this
40677 "deactivate" : true,
40681 * Fires when this panel is resized if fitToFrame is true.
40682 * @param {Roo.ContentPanel} this
40683 * @param {Number} width The width after any component adjustments
40684 * @param {Number} height The height after any component adjustments
40690 * Fires when this tab is created
40691 * @param {Roo.ContentPanel} this
40697 * Fires when this content is scrolled
40698 * @param {Roo.ContentPanel} this
40699 * @param {Event} scrollEvent
40710 if(this.autoScroll && !this.iframe){
40711 this.resizeEl.setStyle("overflow", "auto");
40712 this.resizeEl.on('scroll', this.onScroll, this);
40714 // fix randome scrolling
40715 //this.el.on('scroll', function() {
40716 // Roo.log('fix random scolling');
40717 // this.scrollTo('top',0);
40720 content = content || this.content;
40722 this.setContent(content);
40724 if(config && config.url){
40725 this.setUrl(this.url, this.params, this.loadOnce);
40730 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40732 if (this.view && typeof(this.view.xtype) != 'undefined') {
40733 this.view.el = this.el.appendChild(document.createElement("div"));
40734 this.view = Roo.factory(this.view);
40735 this.view.render && this.view.render(false, '');
40739 this.fireEvent('render', this);
40742 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40752 /* Resize Element - use this to work out scroll etc. */
40755 setRegion : function(region){
40756 this.region = region;
40757 this.setActiveClass(region && !this.background);
40761 setActiveClass: function(state)
40764 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40765 this.el.setStyle('position','relative');
40767 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40768 this.el.setStyle('position', 'absolute');
40773 * Returns the toolbar for this Panel if one was configured.
40774 * @return {Roo.Toolbar}
40776 getToolbar : function(){
40777 return this.toolbar;
40780 setActiveState : function(active)
40782 this.active = active;
40783 this.setActiveClass(active);
40785 if(this.fireEvent("deactivate", this) === false){
40790 this.fireEvent("activate", this);
40794 * Updates this panel's element (not for iframe)
40795 * @param {String} content The new content
40796 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40798 setContent : function(content, loadScripts){
40803 this.el.update(content, loadScripts);
40806 ignoreResize : function(w, h){
40807 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40810 this.lastSize = {width: w, height: h};
40815 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40816 * @return {Roo.UpdateManager} The UpdateManager
40818 getUpdateManager : function(){
40822 return this.el.getUpdateManager();
40825 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40826 * Does not work with IFRAME contents
40827 * @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:
40830 url: "your-url.php",
40831 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40832 callback: yourFunction,
40833 scope: yourObject, //(optional scope)
40836 text: "Loading...",
40842 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40843 * 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.
40844 * @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}
40845 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40846 * @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.
40847 * @return {Roo.ContentPanel} this
40855 var um = this.el.getUpdateManager();
40856 um.update.apply(um, arguments);
40862 * 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.
40863 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40864 * @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)
40865 * @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)
40866 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40868 setUrl : function(url, params, loadOnce){
40870 this.iframeEl.dom.src = url;
40874 if(this.refreshDelegate){
40875 this.removeListener("activate", this.refreshDelegate);
40877 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40878 this.on("activate", this.refreshDelegate);
40879 return this.el.getUpdateManager();
40882 _handleRefresh : function(url, params, loadOnce){
40883 if(!loadOnce || !this.loaded){
40884 var updater = this.el.getUpdateManager();
40885 updater.update(url, params, this._setLoaded.createDelegate(this));
40889 _setLoaded : function(){
40890 this.loaded = true;
40894 * Returns this panel's id
40897 getId : function(){
40902 * Returns this panel's element - used by regiosn to add.
40903 * @return {Roo.Element}
40905 getEl : function(){
40906 return this.wrapEl || this.el;
40911 adjustForComponents : function(width, height)
40913 //Roo.log('adjustForComponents ');
40914 if(this.resizeEl != this.el){
40915 width -= this.el.getFrameWidth('lr');
40916 height -= this.el.getFrameWidth('tb');
40919 var te = this.toolbar.getEl();
40920 te.setWidth(width);
40921 height -= te.getHeight();
40924 var te = this.footer.getEl();
40925 te.setWidth(width);
40926 height -= te.getHeight();
40930 if(this.adjustments){
40931 width += this.adjustments[0];
40932 height += this.adjustments[1];
40934 return {"width": width, "height": height};
40937 setSize : function(width, height){
40938 if(this.fitToFrame && !this.ignoreResize(width, height)){
40939 if(this.fitContainer && this.resizeEl != this.el){
40940 this.el.setSize(width, height);
40942 var size = this.adjustForComponents(width, height);
40944 this.iframeEl.setSize(width,height);
40947 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40948 this.fireEvent('resize', this, size.width, size.height);
40955 * Returns this panel's title
40958 getTitle : function(){
40960 if (typeof(this.title) != 'object') {
40965 for (var k in this.title) {
40966 if (!this.title.hasOwnProperty(k)) {
40970 if (k.indexOf('-') >= 0) {
40971 var s = k.split('-');
40972 for (var i = 0; i<s.length; i++) {
40973 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40976 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40983 * Set this panel's title
40984 * @param {String} title
40986 setTitle : function(title){
40987 this.title = title;
40989 this.region.updatePanelTitle(this, title);
40994 * Returns true is this panel was configured to be closable
40995 * @return {Boolean}
40997 isClosable : function(){
40998 return this.closable;
41001 beforeSlide : function(){
41003 this.resizeEl.clip();
41006 afterSlide : function(){
41008 this.resizeEl.unclip();
41012 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41013 * Will fail silently if the {@link #setUrl} method has not been called.
41014 * This does not activate the panel, just updates its content.
41016 refresh : function(){
41017 if(this.refreshDelegate){
41018 this.loaded = false;
41019 this.refreshDelegate();
41024 * Destroys this panel
41026 destroy : function(){
41027 this.el.removeAllListeners();
41028 var tempEl = document.createElement("span");
41029 tempEl.appendChild(this.el.dom);
41030 tempEl.innerHTML = "";
41036 * form - if the content panel contains a form - this is a reference to it.
41037 * @type {Roo.form.Form}
41041 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41042 * This contains a reference to it.
41048 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41058 * @param {Object} cfg Xtype definition of item to add.
41062 getChildContainer: function () {
41063 return this.getEl();
41067 onScroll : function(e)
41069 this.fireEvent('scroll', this, e);
41074 var ret = new Roo.factory(cfg);
41079 if (cfg.xtype.match(/^Form$/)) {
41082 //if (this.footer) {
41083 // el = this.footer.container.insertSibling(false, 'before');
41085 el = this.el.createChild();
41088 this.form = new Roo.form.Form(cfg);
41091 if ( this.form.allItems.length) {
41092 this.form.render(el.dom);
41096 // should only have one of theses..
41097 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41098 // views.. should not be just added - used named prop 'view''
41100 cfg.el = this.el.appendChild(document.createElement("div"));
41103 var ret = new Roo.factory(cfg);
41105 ret.render && ret.render(false, ''); // render blank..
41115 * @class Roo.bootstrap.panel.Grid
41116 * @extends Roo.bootstrap.panel.Content
41118 * Create a new GridPanel.
41119 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41120 * @param {Object} config A the config object
41126 Roo.bootstrap.panel.Grid = function(config)
41130 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41131 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41133 config.el = this.wrapper;
41134 //this.el = this.wrapper;
41136 if (config.container) {
41137 // ctor'ed from a Border/panel.grid
41140 this.wrapper.setStyle("overflow", "hidden");
41141 this.wrapper.addClass('roo-grid-container');
41146 if(config.toolbar){
41147 var tool_el = this.wrapper.createChild();
41148 this.toolbar = Roo.factory(config.toolbar);
41150 if (config.toolbar.items) {
41151 ti = config.toolbar.items ;
41152 delete config.toolbar.items ;
41156 this.toolbar.render(tool_el);
41157 for(var i =0;i < ti.length;i++) {
41158 // Roo.log(['add child', items[i]]);
41159 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41161 this.toolbar.items = nitems;
41163 delete config.toolbar;
41166 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41167 config.grid.scrollBody = true;;
41168 config.grid.monitorWindowResize = false; // turn off autosizing
41169 config.grid.autoHeight = false;
41170 config.grid.autoWidth = false;
41172 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41174 if (config.background) {
41175 // render grid on panel activation (if panel background)
41176 this.on('activate', function(gp) {
41177 if (!gp.grid.rendered) {
41178 gp.grid.render(this.wrapper);
41179 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41184 this.grid.render(this.wrapper);
41185 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41188 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41189 // ??? needed ??? config.el = this.wrapper;
41194 // xtype created footer. - not sure if will work as we normally have to render first..
41195 if (this.footer && !this.footer.el && this.footer.xtype) {
41197 var ctr = this.grid.getView().getFooterPanel(true);
41198 this.footer.dataSource = this.grid.dataSource;
41199 this.footer = Roo.factory(this.footer, Roo);
41200 this.footer.render(ctr);
41210 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41211 getId : function(){
41212 return this.grid.id;
41216 * Returns the grid for this panel
41217 * @return {Roo.bootstrap.Table}
41219 getGrid : function(){
41223 setSize : function(width, height){
41224 if(!this.ignoreResize(width, height)){
41225 var grid = this.grid;
41226 var size = this.adjustForComponents(width, height);
41227 // tfoot is not a footer?
41230 var gridel = grid.getGridEl();
41231 gridel.setSize(size.width, size.height);
41233 var tbd = grid.getGridEl().select('tbody', true).first();
41234 var thd = grid.getGridEl().select('thead',true).first();
41235 var tbf= grid.getGridEl().select('tfoot', true).first();
41238 size.height -= tbf.getHeight();
41241 size.height -= thd.getHeight();
41244 tbd.setSize(size.width, size.height );
41245 // this is for the account management tab -seems to work there.
41246 var thd = grid.getGridEl().select('thead',true).first();
41248 // tbd.setSize(size.width, size.height - thd.getHeight());
41257 beforeSlide : function(){
41258 this.grid.getView().scroller.clip();
41261 afterSlide : function(){
41262 this.grid.getView().scroller.unclip();
41265 destroy : function(){
41266 this.grid.destroy();
41268 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41273 * @class Roo.bootstrap.panel.Nest
41274 * @extends Roo.bootstrap.panel.Content
41276 * Create a new Panel, that can contain a layout.Border.
41279 * @param {Roo.BorderLayout} layout The layout for this panel
41280 * @param {String/Object} config A string to set only the title or a config object
41282 Roo.bootstrap.panel.Nest = function(config)
41284 // construct with only one argument..
41285 /* FIXME - implement nicer consturctors
41286 if (layout.layout) {
41288 layout = config.layout;
41289 delete config.layout;
41291 if (layout.xtype && !layout.getEl) {
41292 // then layout needs constructing..
41293 layout = Roo.factory(layout, Roo);
41297 config.el = config.layout.getEl();
41299 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41301 config.layout.monitorWindowResize = false; // turn off autosizing
41302 this.layout = config.layout;
41303 this.layout.getEl().addClass("roo-layout-nested-layout");
41304 this.layout.parent = this;
41311 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41313 setSize : function(width, height){
41314 if(!this.ignoreResize(width, height)){
41315 var size = this.adjustForComponents(width, height);
41316 var el = this.layout.getEl();
41317 if (size.height < 1) {
41318 el.setWidth(size.width);
41320 el.setSize(size.width, size.height);
41322 var touch = el.dom.offsetWidth;
41323 this.layout.layout();
41324 // ie requires a double layout on the first pass
41325 if(Roo.isIE && !this.initialized){
41326 this.initialized = true;
41327 this.layout.layout();
41332 // activate all subpanels if not currently active..
41334 setActiveState : function(active){
41335 this.active = active;
41336 this.setActiveClass(active);
41339 this.fireEvent("deactivate", this);
41343 this.fireEvent("activate", this);
41344 // not sure if this should happen before or after..
41345 if (!this.layout) {
41346 return; // should not happen..
41349 for (var r in this.layout.regions) {
41350 reg = this.layout.getRegion(r);
41351 if (reg.getActivePanel()) {
41352 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41353 reg.setActivePanel(reg.getActivePanel());
41356 if (!reg.panels.length) {
41359 reg.showPanel(reg.getPanel(0));
41368 * Returns the nested BorderLayout for this panel
41369 * @return {Roo.BorderLayout}
41371 getLayout : function(){
41372 return this.layout;
41376 * Adds a xtype elements to the layout of the nested panel
41380 xtype : 'ContentPanel',
41387 xtype : 'NestedLayoutPanel',
41393 items : [ ... list of content panels or nested layout panels.. ]
41397 * @param {Object} cfg Xtype definition of item to add.
41399 addxtype : function(cfg) {
41400 return this.layout.addxtype(cfg);
41405 * Ext JS Library 1.1.1
41406 * Copyright(c) 2006-2007, Ext JS, LLC.
41408 * Originally Released Under LGPL - original licence link has changed is not relivant.
41411 * <script type="text/javascript">
41414 * @class Roo.TabPanel
41415 * @extends Roo.util.Observable
41416 * A lightweight tab container.
41420 // basic tabs 1, built from existing content
41421 var tabs = new Roo.TabPanel("tabs1");
41422 tabs.addTab("script", "View Script");
41423 tabs.addTab("markup", "View Markup");
41424 tabs.activate("script");
41426 // more advanced tabs, built from javascript
41427 var jtabs = new Roo.TabPanel("jtabs");
41428 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41430 // set up the UpdateManager
41431 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41432 var updater = tab2.getUpdateManager();
41433 updater.setDefaultUrl("ajax1.htm");
41434 tab2.on('activate', updater.refresh, updater, true);
41436 // Use setUrl for Ajax loading
41437 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41438 tab3.setUrl("ajax2.htm", null, true);
41441 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41444 jtabs.activate("jtabs-1");
41447 * Create a new TabPanel.
41448 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41449 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41451 Roo.bootstrap.panel.Tabs = function(config){
41453 * The container element for this TabPanel.
41454 * @type Roo.Element
41456 this.el = Roo.get(config.el);
41459 if(typeof config == "boolean"){
41460 this.tabPosition = config ? "bottom" : "top";
41462 Roo.apply(this, config);
41466 if(this.tabPosition == "bottom"){
41467 // if tabs are at the bottom = create the body first.
41468 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41469 this.el.addClass("roo-tabs-bottom");
41471 // next create the tabs holders
41473 if (this.tabPosition == "west"){
41475 var reg = this.region; // fake it..
41477 if (!reg.mgr.parent) {
41480 reg = reg.mgr.parent.region;
41482 Roo.log("got nest?");
41484 if (reg.mgr.getRegion('west')) {
41485 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41486 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41487 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41488 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41489 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41497 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41498 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41499 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41500 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41505 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41508 // finally - if tabs are at the top, then create the body last..
41509 if(this.tabPosition != "bottom"){
41510 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41511 * @type Roo.Element
41513 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41514 this.el.addClass("roo-tabs-top");
41518 this.bodyEl.setStyle("position", "relative");
41520 this.active = null;
41521 this.activateDelegate = this.activate.createDelegate(this);
41526 * Fires when the active tab changes
41527 * @param {Roo.TabPanel} this
41528 * @param {Roo.TabPanelItem} activePanel The new active tab
41532 * @event beforetabchange
41533 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41534 * @param {Roo.TabPanel} this
41535 * @param {Object} e Set cancel to true on this object to cancel the tab change
41536 * @param {Roo.TabPanelItem} tab The tab being changed to
41538 "beforetabchange" : true
41541 Roo.EventManager.onWindowResize(this.onResize, this);
41542 this.cpad = this.el.getPadding("lr");
41543 this.hiddenCount = 0;
41546 // toolbar on the tabbar support...
41547 if (this.toolbar) {
41548 alert("no toolbar support yet");
41549 this.toolbar = false;
41551 var tcfg = this.toolbar;
41552 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41553 this.toolbar = new Roo.Toolbar(tcfg);
41554 if (Roo.isSafari) {
41555 var tbl = tcfg.container.child('table', true);
41556 tbl.setAttribute('width', '100%');
41564 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41567 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41569 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41571 tabPosition : "top",
41573 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41575 currentTabWidth : 0,
41577 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41581 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41585 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41587 preferredTabWidth : 175,
41589 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41591 resizeTabs : false,
41593 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41595 monitorResize : true,
41597 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41599 toolbar : false, // set by caller..
41601 region : false, /// set by caller
41603 disableTooltips : true, // not used yet...
41606 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41607 * @param {String} id The id of the div to use <b>or create</b>
41608 * @param {String} text The text for the tab
41609 * @param {String} content (optional) Content to put in the TabPanelItem body
41610 * @param {Boolean} closable (optional) True to create a close icon on the tab
41611 * @return {Roo.TabPanelItem} The created TabPanelItem
41613 addTab : function(id, text, content, closable, tpl)
41615 var item = new Roo.bootstrap.panel.TabItem({
41619 closable : closable,
41622 this.addTabItem(item);
41624 item.setContent(content);
41630 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41631 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41632 * @return {Roo.TabPanelItem}
41634 getTab : function(id){
41635 return this.items[id];
41639 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41640 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41642 hideTab : function(id){
41643 var t = this.items[id];
41646 this.hiddenCount++;
41647 this.autoSizeTabs();
41652 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41653 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41655 unhideTab : function(id){
41656 var t = this.items[id];
41658 t.setHidden(false);
41659 this.hiddenCount--;
41660 this.autoSizeTabs();
41665 * Adds an existing {@link Roo.TabPanelItem}.
41666 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41668 addTabItem : function(item)
41670 this.items[item.id] = item;
41671 this.items.push(item);
41672 this.autoSizeTabs();
41673 // if(this.resizeTabs){
41674 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41675 // this.autoSizeTabs();
41677 // item.autoSize();
41682 * Removes a {@link Roo.TabPanelItem}.
41683 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41685 removeTab : function(id){
41686 var items = this.items;
41687 var tab = items[id];
41688 if(!tab) { return; }
41689 var index = items.indexOf(tab);
41690 if(this.active == tab && items.length > 1){
41691 var newTab = this.getNextAvailable(index);
41696 this.stripEl.dom.removeChild(tab.pnode.dom);
41697 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41698 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41700 items.splice(index, 1);
41701 delete this.items[tab.id];
41702 tab.fireEvent("close", tab);
41703 tab.purgeListeners();
41704 this.autoSizeTabs();
41707 getNextAvailable : function(start){
41708 var items = this.items;
41710 // look for a next tab that will slide over to
41711 // replace the one being removed
41712 while(index < items.length){
41713 var item = items[++index];
41714 if(item && !item.isHidden()){
41718 // if one isn't found select the previous tab (on the left)
41721 var item = items[--index];
41722 if(item && !item.isHidden()){
41730 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41731 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41733 disableTab : function(id){
41734 var tab = this.items[id];
41735 if(tab && this.active != tab){
41741 * Enables a {@link Roo.TabPanelItem} that is disabled.
41742 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41744 enableTab : function(id){
41745 var tab = this.items[id];
41750 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41751 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41752 * @return {Roo.TabPanelItem} The TabPanelItem.
41754 activate : function(id)
41756 //Roo.log('activite:' + id);
41758 var tab = this.items[id];
41762 if(tab == this.active || tab.disabled){
41766 this.fireEvent("beforetabchange", this, e, tab);
41767 if(e.cancel !== true && !tab.disabled){
41769 this.active.hide();
41771 this.active = this.items[id];
41772 this.active.show();
41773 this.fireEvent("tabchange", this, this.active);
41779 * Gets the active {@link Roo.TabPanelItem}.
41780 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41782 getActiveTab : function(){
41783 return this.active;
41787 * Updates the tab body element to fit the height of the container element
41788 * for overflow scrolling
41789 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41791 syncHeight : function(targetHeight){
41792 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41793 var bm = this.bodyEl.getMargins();
41794 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41795 this.bodyEl.setHeight(newHeight);
41799 onResize : function(){
41800 if(this.monitorResize){
41801 this.autoSizeTabs();
41806 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41808 beginUpdate : function(){
41809 this.updating = true;
41813 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41815 endUpdate : function(){
41816 this.updating = false;
41817 this.autoSizeTabs();
41821 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41823 autoSizeTabs : function()
41825 var count = this.items.length;
41826 var vcount = count - this.hiddenCount;
41829 this.stripEl.hide();
41831 this.stripEl.show();
41834 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41839 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41840 var availWidth = Math.floor(w / vcount);
41841 var b = this.stripBody;
41842 if(b.getWidth() > w){
41843 var tabs = this.items;
41844 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41845 if(availWidth < this.minTabWidth){
41846 /*if(!this.sleft){ // incomplete scrolling code
41847 this.createScrollButtons();
41850 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41853 if(this.currentTabWidth < this.preferredTabWidth){
41854 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41860 * Returns the number of tabs in this TabPanel.
41863 getCount : function(){
41864 return this.items.length;
41868 * Resizes all the tabs to the passed width
41869 * @param {Number} The new width
41871 setTabWidth : function(width){
41872 this.currentTabWidth = width;
41873 for(var i = 0, len = this.items.length; i < len; i++) {
41874 if(!this.items[i].isHidden()) {
41875 this.items[i].setWidth(width);
41881 * Destroys this TabPanel
41882 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41884 destroy : function(removeEl){
41885 Roo.EventManager.removeResizeListener(this.onResize, this);
41886 for(var i = 0, len = this.items.length; i < len; i++){
41887 this.items[i].purgeListeners();
41889 if(removeEl === true){
41890 this.el.update("");
41895 createStrip : function(container)
41897 var strip = document.createElement("nav");
41898 strip.className = Roo.bootstrap.version == 4 ?
41899 "navbar-light bg-light" :
41900 "navbar navbar-default"; //"x-tabs-wrap";
41901 container.appendChild(strip);
41905 createStripList : function(strip)
41907 // div wrapper for retard IE
41908 // returns the "tr" element.
41909 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41910 //'<div class="x-tabs-strip-wrap">'+
41911 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41912 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41913 return strip.firstChild; //.firstChild.firstChild.firstChild;
41915 createBody : function(container)
41917 var body = document.createElement("div");
41918 Roo.id(body, "tab-body");
41919 //Roo.fly(body).addClass("x-tabs-body");
41920 Roo.fly(body).addClass("tab-content");
41921 container.appendChild(body);
41924 createItemBody :function(bodyEl, id){
41925 var body = Roo.getDom(id);
41927 body = document.createElement("div");
41930 //Roo.fly(body).addClass("x-tabs-item-body");
41931 Roo.fly(body).addClass("tab-pane");
41932 bodyEl.insertBefore(body, bodyEl.firstChild);
41936 createStripElements : function(stripEl, text, closable, tpl)
41938 var td = document.createElement("li"); // was td..
41939 td.className = 'nav-item';
41941 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41944 stripEl.appendChild(td);
41946 td.className = "x-tabs-closable";
41947 if(!this.closeTpl){
41948 this.closeTpl = new Roo.Template(
41949 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41950 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41951 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41954 var el = this.closeTpl.overwrite(td, {"text": text});
41955 var close = el.getElementsByTagName("div")[0];
41956 var inner = el.getElementsByTagName("em")[0];
41957 return {"el": el, "close": close, "inner": inner};
41960 // not sure what this is..
41961 // if(!this.tabTpl){
41962 //this.tabTpl = new Roo.Template(
41963 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41964 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41966 // this.tabTpl = new Roo.Template(
41967 // '<a href="#">' +
41968 // '<span unselectable="on"' +
41969 // (this.disableTooltips ? '' : ' title="{text}"') +
41970 // ' >{text}</span></a>'
41976 var template = tpl || this.tabTpl || false;
41979 template = new Roo.Template(
41980 Roo.bootstrap.version == 4 ?
41982 '<a class="nav-link" href="#" unselectable="on"' +
41983 (this.disableTooltips ? '' : ' title="{text}"') +
41986 '<a class="nav-link" href="#">' +
41987 '<span unselectable="on"' +
41988 (this.disableTooltips ? '' : ' title="{text}"') +
41989 ' >{text}</span></a>'
41994 switch (typeof(template)) {
41998 template = new Roo.Template(template);
42004 var el = template.overwrite(td, {"text": text});
42006 var inner = el.getElementsByTagName("span")[0];
42008 return {"el": el, "inner": inner};
42016 * @class Roo.TabPanelItem
42017 * @extends Roo.util.Observable
42018 * Represents an individual item (tab plus body) in a TabPanel.
42019 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42020 * @param {String} id The id of this TabPanelItem
42021 * @param {String} text The text for the tab of this TabPanelItem
42022 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42024 Roo.bootstrap.panel.TabItem = function(config){
42026 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42027 * @type Roo.TabPanel
42029 this.tabPanel = config.panel;
42031 * The id for this TabPanelItem
42034 this.id = config.id;
42036 this.disabled = false;
42038 this.text = config.text;
42040 this.loaded = false;
42041 this.closable = config.closable;
42044 * The body element for this TabPanelItem.
42045 * @type Roo.Element
42047 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42048 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42049 this.bodyEl.setStyle("display", "block");
42050 this.bodyEl.setStyle("zoom", "1");
42051 //this.hideAction();
42053 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42055 this.el = Roo.get(els.el);
42056 this.inner = Roo.get(els.inner, true);
42057 this.textEl = Roo.bootstrap.version == 4 ?
42058 this.el : Roo.get(this.el.dom.firstChild, true);
42060 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42061 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42064 // this.el.on("mousedown", this.onTabMouseDown, this);
42065 this.el.on("click", this.onTabClick, this);
42067 if(config.closable){
42068 var c = Roo.get(els.close, true);
42069 c.dom.title = this.closeText;
42070 c.addClassOnOver("close-over");
42071 c.on("click", this.closeClick, this);
42077 * Fires when this tab becomes the active tab.
42078 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42079 * @param {Roo.TabPanelItem} this
42083 * @event beforeclose
42084 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42085 * @param {Roo.TabPanelItem} this
42086 * @param {Object} e Set cancel to true on this object to cancel the close.
42088 "beforeclose": true,
42091 * Fires when this tab is closed.
42092 * @param {Roo.TabPanelItem} this
42096 * @event deactivate
42097 * Fires when this tab is no longer the active tab.
42098 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42099 * @param {Roo.TabPanelItem} this
42101 "deactivate" : true
42103 this.hidden = false;
42105 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42108 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42110 purgeListeners : function(){
42111 Roo.util.Observable.prototype.purgeListeners.call(this);
42112 this.el.removeAllListeners();
42115 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42118 this.status_node.addClass("active");
42121 this.tabPanel.stripWrap.repaint();
42123 this.fireEvent("activate", this.tabPanel, this);
42127 * Returns true if this tab is the active tab.
42128 * @return {Boolean}
42130 isActive : function(){
42131 return this.tabPanel.getActiveTab() == this;
42135 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42138 this.status_node.removeClass("active");
42140 this.fireEvent("deactivate", this.tabPanel, this);
42143 hideAction : function(){
42144 this.bodyEl.hide();
42145 this.bodyEl.setStyle("position", "absolute");
42146 this.bodyEl.setLeft("-20000px");
42147 this.bodyEl.setTop("-20000px");
42150 showAction : function(){
42151 this.bodyEl.setStyle("position", "relative");
42152 this.bodyEl.setTop("");
42153 this.bodyEl.setLeft("");
42154 this.bodyEl.show();
42158 * Set the tooltip for the tab.
42159 * @param {String} tooltip The tab's tooltip
42161 setTooltip : function(text){
42162 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42163 this.textEl.dom.qtip = text;
42164 this.textEl.dom.removeAttribute('title');
42166 this.textEl.dom.title = text;
42170 onTabClick : function(e){
42171 e.preventDefault();
42172 this.tabPanel.activate(this.id);
42175 onTabMouseDown : function(e){
42176 e.preventDefault();
42177 this.tabPanel.activate(this.id);
42180 getWidth : function(){
42181 return this.inner.getWidth();
42184 setWidth : function(width){
42185 var iwidth = width - this.linode.getPadding("lr");
42186 this.inner.setWidth(iwidth);
42187 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42188 this.linode.setWidth(width);
42192 * Show or hide the tab
42193 * @param {Boolean} hidden True to hide or false to show.
42195 setHidden : function(hidden){
42196 this.hidden = hidden;
42197 this.linode.setStyle("display", hidden ? "none" : "");
42201 * Returns true if this tab is "hidden"
42202 * @return {Boolean}
42204 isHidden : function(){
42205 return this.hidden;
42209 * Returns the text for this tab
42212 getText : function(){
42216 autoSize : function(){
42217 //this.el.beginMeasure();
42218 this.textEl.setWidth(1);
42220 * #2804 [new] Tabs in Roojs
42221 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42223 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42224 //this.el.endMeasure();
42228 * Sets the text for the tab (Note: this also sets the tooltip text)
42229 * @param {String} text The tab's text and tooltip
42231 setText : function(text){
42233 this.textEl.update(text);
42234 this.setTooltip(text);
42235 //if(!this.tabPanel.resizeTabs){
42236 // this.autoSize();
42240 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42242 activate : function(){
42243 this.tabPanel.activate(this.id);
42247 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42249 disable : function(){
42250 if(this.tabPanel.active != this){
42251 this.disabled = true;
42252 this.status_node.addClass("disabled");
42257 * Enables this TabPanelItem if it was previously disabled.
42259 enable : function(){
42260 this.disabled = false;
42261 this.status_node.removeClass("disabled");
42265 * Sets the content for this TabPanelItem.
42266 * @param {String} content The content
42267 * @param {Boolean} loadScripts true to look for and load scripts
42269 setContent : function(content, loadScripts){
42270 this.bodyEl.update(content, loadScripts);
42274 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42275 * @return {Roo.UpdateManager} The UpdateManager
42277 getUpdateManager : function(){
42278 return this.bodyEl.getUpdateManager();
42282 * Set a URL to be used to load the content for this TabPanelItem.
42283 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42284 * @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)
42285 * @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)
42286 * @return {Roo.UpdateManager} The UpdateManager
42288 setUrl : function(url, params, loadOnce){
42289 if(this.refreshDelegate){
42290 this.un('activate', this.refreshDelegate);
42292 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42293 this.on("activate", this.refreshDelegate);
42294 return this.bodyEl.getUpdateManager();
42298 _handleRefresh : function(url, params, loadOnce){
42299 if(!loadOnce || !this.loaded){
42300 var updater = this.bodyEl.getUpdateManager();
42301 updater.update(url, params, this._setLoaded.createDelegate(this));
42306 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42307 * Will fail silently if the setUrl method has not been called.
42308 * This does not activate the panel, just updates its content.
42310 refresh : function(){
42311 if(this.refreshDelegate){
42312 this.loaded = false;
42313 this.refreshDelegate();
42318 _setLoaded : function(){
42319 this.loaded = true;
42323 closeClick : function(e){
42326 this.fireEvent("beforeclose", this, o);
42327 if(o.cancel !== true){
42328 this.tabPanel.removeTab(this.id);
42332 * The text displayed in the tooltip for the close icon.
42335 closeText : "Close this tab"
42338 * This script refer to:
42339 * Title: International Telephone Input
42340 * Author: Jack O'Connor
42341 * Code version: v12.1.12
42342 * Availability: https://github.com/jackocnr/intl-tel-input.git
42345 Roo.bootstrap.PhoneInputData = function() {
42348 "Afghanistan (افغانستان)",
42353 "Albania (Shqipëri)",
42358 "Algeria (الجزائر)",
42383 "Antigua and Barbuda",
42393 "Armenia (Հայաստան)",
42409 "Austria (Österreich)",
42414 "Azerbaijan (Azərbaycan)",
42424 "Bahrain (البحرين)",
42429 "Bangladesh (বাংলাদেশ)",
42439 "Belarus (Беларусь)",
42444 "Belgium (België)",
42474 "Bosnia and Herzegovina (Босна и Херцеговина)",
42489 "British Indian Ocean Territory",
42494 "British Virgin Islands",
42504 "Bulgaria (България)",
42514 "Burundi (Uburundi)",
42519 "Cambodia (កម្ពុជា)",
42524 "Cameroon (Cameroun)",
42533 ["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"]
42536 "Cape Verde (Kabu Verdi)",
42541 "Caribbean Netherlands",
42552 "Central African Republic (République centrafricaine)",
42572 "Christmas Island",
42578 "Cocos (Keeling) Islands",
42589 "Comoros (جزر القمر)",
42594 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42599 "Congo (Republic) (Congo-Brazzaville)",
42619 "Croatia (Hrvatska)",
42640 "Czech Republic (Česká republika)",
42645 "Denmark (Danmark)",
42660 "Dominican Republic (República Dominicana)",
42664 ["809", "829", "849"]
42682 "Equatorial Guinea (Guinea Ecuatorial)",
42702 "Falkland Islands (Islas Malvinas)",
42707 "Faroe Islands (Føroyar)",
42728 "French Guiana (Guyane française)",
42733 "French Polynesia (Polynésie française)",
42748 "Georgia (საქართველო)",
42753 "Germany (Deutschland)",
42773 "Greenland (Kalaallit Nunaat)",
42810 "Guinea-Bissau (Guiné Bissau)",
42835 "Hungary (Magyarország)",
42840 "Iceland (Ísland)",
42860 "Iraq (العراق)",
42876 "Israel (ישראל)",
42903 "Jordan (الأردن)",
42908 "Kazakhstan (Казахстан)",
42929 "Kuwait (الكويت)",
42934 "Kyrgyzstan (Кыргызстан)",
42944 "Latvia (Latvija)",
42949 "Lebanon (لبنان)",
42964 "Libya (ليبيا)",
42974 "Lithuania (Lietuva)",
42989 "Macedonia (FYROM) (Македонија)",
42994 "Madagascar (Madagasikara)",
43024 "Marshall Islands",
43034 "Mauritania (موريتانيا)",
43039 "Mauritius (Moris)",
43060 "Moldova (Republica Moldova)",
43070 "Mongolia (Монгол)",
43075 "Montenegro (Crna Gora)",
43085 "Morocco (المغرب)",
43091 "Mozambique (Moçambique)",
43096 "Myanmar (Burma) (မြန်မာ)",
43101 "Namibia (Namibië)",
43116 "Netherlands (Nederland)",
43121 "New Caledonia (Nouvelle-Calédonie)",
43156 "North Korea (조선 민주주의 인민 공화국)",
43161 "Northern Mariana Islands",
43177 "Pakistan (پاکستان)",
43187 "Palestine (فلسطين)",
43197 "Papua New Guinea",
43239 "Réunion (La Réunion)",
43245 "Romania (România)",
43261 "Saint Barthélemy",
43272 "Saint Kitts and Nevis",
43282 "Saint Martin (Saint-Martin (partie française))",
43288 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43293 "Saint Vincent and the Grenadines",
43308 "São Tomé and Príncipe (São Tomé e Príncipe)",
43313 "Saudi Arabia (المملكة العربية السعودية)",
43318 "Senegal (Sénégal)",
43348 "Slovakia (Slovensko)",
43353 "Slovenia (Slovenija)",
43363 "Somalia (Soomaaliya)",
43373 "South Korea (대한민국)",
43378 "South Sudan (جنوب السودان)",
43388 "Sri Lanka (ශ්රී ලංකාව)",
43393 "Sudan (السودان)",
43403 "Svalbard and Jan Mayen",
43414 "Sweden (Sverige)",
43419 "Switzerland (Schweiz)",
43424 "Syria (سوريا)",
43469 "Trinidad and Tobago",
43474 "Tunisia (تونس)",
43479 "Turkey (Türkiye)",
43489 "Turks and Caicos Islands",
43499 "U.S. Virgin Islands",
43509 "Ukraine (Україна)",
43514 "United Arab Emirates (الإمارات العربية المتحدة)",
43536 "Uzbekistan (Oʻzbekiston)",
43546 "Vatican City (Città del Vaticano)",
43557 "Vietnam (Việt Nam)",
43562 "Wallis and Futuna (Wallis-et-Futuna)",
43567 "Western Sahara (الصحراء الغربية)",
43573 "Yemen (اليمن)",
43597 * This script refer to:
43598 * Title: International Telephone Input
43599 * Author: Jack O'Connor
43600 * Code version: v12.1.12
43601 * Availability: https://github.com/jackocnr/intl-tel-input.git
43605 * @class Roo.bootstrap.PhoneInput
43606 * @extends Roo.bootstrap.TriggerField
43607 * An input with International dial-code selection
43609 * @cfg {String} defaultDialCode default '+852'
43610 * @cfg {Array} preferedCountries default []
43613 * Create a new PhoneInput.
43614 * @param {Object} config Configuration options
43617 Roo.bootstrap.PhoneInput = function(config) {
43618 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43621 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43623 listWidth: undefined,
43625 selectedClass: 'active',
43627 invalidClass : "has-warning",
43629 validClass: 'has-success',
43631 allowed: '0123456789',
43636 * @cfg {String} defaultDialCode The default dial code when initializing the input
43638 defaultDialCode: '+852',
43641 * @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
43643 preferedCountries: false,
43645 getAutoCreate : function()
43647 var data = Roo.bootstrap.PhoneInputData();
43648 var align = this.labelAlign || this.parentLabelAlign();
43651 this.allCountries = [];
43652 this.dialCodeMapping = [];
43654 for (var i = 0; i < data.length; i++) {
43656 this.allCountries[i] = {
43660 priority: c[3] || 0,
43661 areaCodes: c[4] || null
43663 this.dialCodeMapping[c[2]] = {
43666 priority: c[3] || 0,
43667 areaCodes: c[4] || null
43679 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43680 maxlength: this.max_length,
43681 cls : 'form-control tel-input',
43682 autocomplete: 'new-password'
43685 var hiddenInput = {
43688 cls: 'hidden-tel-input'
43692 hiddenInput.name = this.name;
43695 if (this.disabled) {
43696 input.disabled = true;
43699 var flag_container = {
43716 cls: this.hasFeedback ? 'has-feedback' : '',
43722 cls: 'dial-code-holder',
43729 cls: 'roo-select2-container input-group',
43736 if (this.fieldLabel.length) {
43739 tooltip: 'This field is required'
43745 cls: 'control-label',
43751 html: this.fieldLabel
43754 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43760 if(this.indicatorpos == 'right') {
43761 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43768 if(align == 'left') {
43776 if(this.labelWidth > 12){
43777 label.style = "width: " + this.labelWidth + 'px';
43779 if(this.labelWidth < 13 && this.labelmd == 0){
43780 this.labelmd = this.labelWidth;
43782 if(this.labellg > 0){
43783 label.cls += ' col-lg-' + this.labellg;
43784 input.cls += ' col-lg-' + (12 - this.labellg);
43786 if(this.labelmd > 0){
43787 label.cls += ' col-md-' + this.labelmd;
43788 container.cls += ' col-md-' + (12 - this.labelmd);
43790 if(this.labelsm > 0){
43791 label.cls += ' col-sm-' + this.labelsm;
43792 container.cls += ' col-sm-' + (12 - this.labelsm);
43794 if(this.labelxs > 0){
43795 label.cls += ' col-xs-' + this.labelxs;
43796 container.cls += ' col-xs-' + (12 - this.labelxs);
43806 var settings = this;
43808 ['xs','sm','md','lg'].map(function(size){
43809 if (settings[size]) {
43810 cfg.cls += ' col-' + size + '-' + settings[size];
43814 this.store = new Roo.data.Store({
43815 proxy : new Roo.data.MemoryProxy({}),
43816 reader : new Roo.data.JsonReader({
43827 'name' : 'dialCode',
43831 'name' : 'priority',
43835 'name' : 'areaCodes',
43842 if(!this.preferedCountries) {
43843 this.preferedCountries = [
43850 var p = this.preferedCountries.reverse();
43853 for (var i = 0; i < p.length; i++) {
43854 for (var j = 0; j < this.allCountries.length; j++) {
43855 if(this.allCountries[j].iso2 == p[i]) {
43856 var t = this.allCountries[j];
43857 this.allCountries.splice(j,1);
43858 this.allCountries.unshift(t);
43864 this.store.proxy.data = {
43866 data: this.allCountries
43872 initEvents : function()
43875 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43877 this.indicator = this.indicatorEl();
43878 this.flag = this.flagEl();
43879 this.dialCodeHolder = this.dialCodeHolderEl();
43881 this.trigger = this.el.select('div.flag-box',true).first();
43882 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43887 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43888 _this.list.setWidth(lw);
43891 this.list.on('mouseover', this.onViewOver, this);
43892 this.list.on('mousemove', this.onViewMove, this);
43893 this.inputEl().on("keyup", this.onKeyUp, this);
43894 this.inputEl().on("keypress", this.onKeyPress, this);
43896 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43898 this.view = new Roo.View(this.list, this.tpl, {
43899 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43902 this.view.on('click', this.onViewClick, this);
43903 this.setValue(this.defaultDialCode);
43906 onTriggerClick : function(e)
43908 Roo.log('trigger click');
43913 if(this.isExpanded()){
43915 this.hasFocus = false;
43917 this.store.load({});
43918 this.hasFocus = true;
43923 isExpanded : function()
43925 return this.list.isVisible();
43928 collapse : function()
43930 if(!this.isExpanded()){
43934 Roo.get(document).un('mousedown', this.collapseIf, this);
43935 Roo.get(document).un('mousewheel', this.collapseIf, this);
43936 this.fireEvent('collapse', this);
43940 expand : function()
43944 if(this.isExpanded() || !this.hasFocus){
43948 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43949 this.list.setWidth(lw);
43952 this.restrictHeight();
43954 Roo.get(document).on('mousedown', this.collapseIf, this);
43955 Roo.get(document).on('mousewheel', this.collapseIf, this);
43957 this.fireEvent('expand', this);
43960 restrictHeight : function()
43962 this.list.alignTo(this.inputEl(), this.listAlign);
43963 this.list.alignTo(this.inputEl(), this.listAlign);
43966 onViewOver : function(e, t)
43968 if(this.inKeyMode){
43971 var item = this.view.findItemFromChild(t);
43974 var index = this.view.indexOf(item);
43975 this.select(index, false);
43980 onViewClick : function(view, doFocus, el, e)
43982 var index = this.view.getSelectedIndexes()[0];
43984 var r = this.store.getAt(index);
43987 this.onSelect(r, index);
43989 if(doFocus !== false && !this.blockFocus){
43990 this.inputEl().focus();
43994 onViewMove : function(e, t)
43996 this.inKeyMode = false;
43999 select : function(index, scrollIntoView)
44001 this.selectedIndex = index;
44002 this.view.select(index);
44003 if(scrollIntoView !== false){
44004 var el = this.view.getNode(index);
44006 this.list.scrollChildIntoView(el, false);
44011 createList : function()
44013 this.list = Roo.get(document.body).createChild({
44015 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44016 style: 'display:none'
44019 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44022 collapseIf : function(e)
44024 var in_combo = e.within(this.el);
44025 var in_list = e.within(this.list);
44026 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44028 if (in_combo || in_list || is_list) {
44034 onSelect : function(record, index)
44036 if(this.fireEvent('beforeselect', this, record, index) !== false){
44038 this.setFlagClass(record.data.iso2);
44039 this.setDialCode(record.data.dialCode);
44040 this.hasFocus = false;
44042 this.fireEvent('select', this, record, index);
44046 flagEl : function()
44048 var flag = this.el.select('div.flag',true).first();
44055 dialCodeHolderEl : function()
44057 var d = this.el.select('input.dial-code-holder',true).first();
44064 setDialCode : function(v)
44066 this.dialCodeHolder.dom.value = '+'+v;
44069 setFlagClass : function(n)
44071 this.flag.dom.className = 'flag '+n;
44074 getValue : function()
44076 var v = this.inputEl().getValue();
44077 if(this.dialCodeHolder) {
44078 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44083 setValue : function(v)
44085 var d = this.getDialCode(v);
44087 //invalid dial code
44088 if(v.length == 0 || !d || d.length == 0) {
44090 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44091 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44097 this.setFlagClass(this.dialCodeMapping[d].iso2);
44098 this.setDialCode(d);
44099 this.inputEl().dom.value = v.replace('+'+d,'');
44100 this.hiddenEl().dom.value = this.getValue();
44105 getDialCode : function(v)
44109 if (v.length == 0) {
44110 return this.dialCodeHolder.dom.value;
44114 if (v.charAt(0) != "+") {
44117 var numericChars = "";
44118 for (var i = 1; i < v.length; i++) {
44119 var c = v.charAt(i);
44122 if (this.dialCodeMapping[numericChars]) {
44123 dialCode = v.substr(1, i);
44125 if (numericChars.length == 4) {
44135 this.setValue(this.defaultDialCode);
44139 hiddenEl : function()
44141 return this.el.select('input.hidden-tel-input',true).first();
44144 // after setting val
44145 onKeyUp : function(e){
44146 this.setValue(this.getValue());
44149 onKeyPress : function(e){
44150 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44157 * @class Roo.bootstrap.MoneyField
44158 * @extends Roo.bootstrap.ComboBox
44159 * Bootstrap MoneyField class
44162 * Create a new MoneyField.
44163 * @param {Object} config Configuration options
44166 Roo.bootstrap.MoneyField = function(config) {
44168 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44172 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44175 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44177 allowDecimals : true,
44179 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44181 decimalSeparator : ".",
44183 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44185 decimalPrecision : 0,
44187 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44189 allowNegative : true,
44191 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44195 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44197 minValue : Number.NEGATIVE_INFINITY,
44199 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44201 maxValue : Number.MAX_VALUE,
44203 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44205 minText : "The minimum value for this field is {0}",
44207 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44209 maxText : "The maximum value for this field is {0}",
44211 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44212 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44214 nanText : "{0} is not a valid number",
44216 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44220 * @cfg {String} defaults currency of the MoneyField
44221 * value should be in lkey
44223 defaultCurrency : false,
44225 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44227 thousandsDelimiter : false,
44229 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44240 getAutoCreate : function()
44242 var align = this.labelAlign || this.parentLabelAlign();
44254 cls : 'form-control roo-money-amount-input',
44255 autocomplete: 'new-password'
44258 var hiddenInput = {
44262 cls: 'hidden-number-input'
44265 if(this.max_length) {
44266 input.maxlength = this.max_length;
44270 hiddenInput.name = this.name;
44273 if (this.disabled) {
44274 input.disabled = true;
44277 var clg = 12 - this.inputlg;
44278 var cmd = 12 - this.inputmd;
44279 var csm = 12 - this.inputsm;
44280 var cxs = 12 - this.inputxs;
44284 cls : 'row roo-money-field',
44288 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44292 cls: 'roo-select2-container input-group',
44296 cls : 'form-control roo-money-currency-input',
44297 autocomplete: 'new-password',
44299 name : this.currencyName
44303 cls : 'input-group-addon',
44317 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44321 cls: this.hasFeedback ? 'has-feedback' : '',
44332 if (this.fieldLabel.length) {
44335 tooltip: 'This field is required'
44341 cls: 'control-label',
44347 html: this.fieldLabel
44350 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44356 if(this.indicatorpos == 'right') {
44357 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44364 if(align == 'left') {
44372 if(this.labelWidth > 12){
44373 label.style = "width: " + this.labelWidth + 'px';
44375 if(this.labelWidth < 13 && this.labelmd == 0){
44376 this.labelmd = this.labelWidth;
44378 if(this.labellg > 0){
44379 label.cls += ' col-lg-' + this.labellg;
44380 input.cls += ' col-lg-' + (12 - this.labellg);
44382 if(this.labelmd > 0){
44383 label.cls += ' col-md-' + this.labelmd;
44384 container.cls += ' col-md-' + (12 - this.labelmd);
44386 if(this.labelsm > 0){
44387 label.cls += ' col-sm-' + this.labelsm;
44388 container.cls += ' col-sm-' + (12 - this.labelsm);
44390 if(this.labelxs > 0){
44391 label.cls += ' col-xs-' + this.labelxs;
44392 container.cls += ' col-xs-' + (12 - this.labelxs);
44403 var settings = this;
44405 ['xs','sm','md','lg'].map(function(size){
44406 if (settings[size]) {
44407 cfg.cls += ' col-' + size + '-' + settings[size];
44414 initEvents : function()
44416 this.indicator = this.indicatorEl();
44418 this.initCurrencyEvent();
44420 this.initNumberEvent();
44423 initCurrencyEvent : function()
44426 throw "can not find store for combo";
44429 this.store = Roo.factory(this.store, Roo.data);
44430 this.store.parent = this;
44434 this.triggerEl = this.el.select('.input-group-addon', true).first();
44436 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44441 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44442 _this.list.setWidth(lw);
44445 this.list.on('mouseover', this.onViewOver, this);
44446 this.list.on('mousemove', this.onViewMove, this);
44447 this.list.on('scroll', this.onViewScroll, this);
44450 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44453 this.view = new Roo.View(this.list, this.tpl, {
44454 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44457 this.view.on('click', this.onViewClick, this);
44459 this.store.on('beforeload', this.onBeforeLoad, this);
44460 this.store.on('load', this.onLoad, this);
44461 this.store.on('loadexception', this.onLoadException, this);
44463 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44464 "up" : function(e){
44465 this.inKeyMode = true;
44469 "down" : function(e){
44470 if(!this.isExpanded()){
44471 this.onTriggerClick();
44473 this.inKeyMode = true;
44478 "enter" : function(e){
44481 if(this.fireEvent("specialkey", this, e)){
44482 this.onViewClick(false);
44488 "esc" : function(e){
44492 "tab" : function(e){
44495 if(this.fireEvent("specialkey", this, e)){
44496 this.onViewClick(false);
44504 doRelay : function(foo, bar, hname){
44505 if(hname == 'down' || this.scope.isExpanded()){
44506 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44514 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44518 initNumberEvent : function(e)
44520 this.inputEl().on("keydown" , this.fireKey, this);
44521 this.inputEl().on("focus", this.onFocus, this);
44522 this.inputEl().on("blur", this.onBlur, this);
44524 this.inputEl().relayEvent('keyup', this);
44526 if(this.indicator){
44527 this.indicator.addClass('invisible');
44530 this.originalValue = this.getValue();
44532 if(this.validationEvent == 'keyup'){
44533 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44534 this.inputEl().on('keyup', this.filterValidation, this);
44536 else if(this.validationEvent !== false){
44537 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44540 if(this.selectOnFocus){
44541 this.on("focus", this.preFocus, this);
44544 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44545 this.inputEl().on("keypress", this.filterKeys, this);
44547 this.inputEl().relayEvent('keypress', this);
44550 var allowed = "0123456789";
44552 if(this.allowDecimals){
44553 allowed += this.decimalSeparator;
44556 if(this.allowNegative){
44560 if(this.thousandsDelimiter) {
44564 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44566 var keyPress = function(e){
44568 var k = e.getKey();
44570 var c = e.getCharCode();
44573 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44574 allowed.indexOf(String.fromCharCode(c)) === -1
44580 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44584 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44589 this.inputEl().on("keypress", keyPress, this);
44593 onTriggerClick : function(e)
44600 this.loadNext = false;
44602 if(this.isExpanded()){
44607 this.hasFocus = true;
44609 if(this.triggerAction == 'all') {
44610 this.doQuery(this.allQuery, true);
44614 this.doQuery(this.getRawValue());
44617 getCurrency : function()
44619 var v = this.currencyEl().getValue();
44624 restrictHeight : function()
44626 this.list.alignTo(this.currencyEl(), this.listAlign);
44627 this.list.alignTo(this.currencyEl(), this.listAlign);
44630 onViewClick : function(view, doFocus, el, e)
44632 var index = this.view.getSelectedIndexes()[0];
44634 var r = this.store.getAt(index);
44637 this.onSelect(r, index);
44641 onSelect : function(record, index){
44643 if(this.fireEvent('beforeselect', this, record, index) !== false){
44645 this.setFromCurrencyData(index > -1 ? record.data : false);
44649 this.fireEvent('select', this, record, index);
44653 setFromCurrencyData : function(o)
44657 this.lastCurrency = o;
44659 if (this.currencyField) {
44660 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44662 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44665 this.lastSelectionText = currency;
44667 //setting default currency
44668 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44669 this.setCurrency(this.defaultCurrency);
44673 this.setCurrency(currency);
44676 setFromData : function(o)
44680 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44682 this.setFromCurrencyData(c);
44687 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44689 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44692 this.setValue(value);
44696 setCurrency : function(v)
44698 this.currencyValue = v;
44701 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44706 setValue : function(v)
44708 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44714 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44716 this.inputEl().dom.value = (v == '') ? '' :
44717 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44719 if(!this.allowZero && v === '0') {
44720 this.hiddenEl().dom.value = '';
44721 this.inputEl().dom.value = '';
44728 getRawValue : function()
44730 var v = this.inputEl().getValue();
44735 getValue : function()
44737 return this.fixPrecision(this.parseValue(this.getRawValue()));
44740 parseValue : function(value)
44742 if(this.thousandsDelimiter) {
44744 r = new RegExp(",", "g");
44745 value = value.replace(r, "");
44748 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44749 return isNaN(value) ? '' : value;
44753 fixPrecision : function(value)
44755 if(this.thousandsDelimiter) {
44757 r = new RegExp(",", "g");
44758 value = value.replace(r, "");
44761 var nan = isNaN(value);
44763 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44764 return nan ? '' : value;
44766 return parseFloat(value).toFixed(this.decimalPrecision);
44769 decimalPrecisionFcn : function(v)
44771 return Math.floor(v);
44774 validateValue : function(value)
44776 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44780 var num = this.parseValue(value);
44783 this.markInvalid(String.format(this.nanText, value));
44787 if(num < this.minValue){
44788 this.markInvalid(String.format(this.minText, this.minValue));
44792 if(num > this.maxValue){
44793 this.markInvalid(String.format(this.maxText, this.maxValue));
44800 validate : function()
44802 if(this.disabled || this.allowBlank){
44807 var currency = this.getCurrency();
44809 if(this.validateValue(this.getRawValue()) && currency.length){
44814 this.markInvalid();
44818 getName: function()
44823 beforeBlur : function()
44829 var v = this.parseValue(this.getRawValue());
44836 onBlur : function()
44840 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44841 //this.el.removeClass(this.focusClass);
44844 this.hasFocus = false;
44846 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44850 var v = this.getValue();
44852 if(String(v) !== String(this.startValue)){
44853 this.fireEvent('change', this, v, this.startValue);
44856 this.fireEvent("blur", this);
44859 inputEl : function()
44861 return this.el.select('.roo-money-amount-input', true).first();
44864 currencyEl : function()
44866 return this.el.select('.roo-money-currency-input', true).first();
44869 hiddenEl : function()
44871 return this.el.select('input.hidden-number-input',true).first();
44875 * @class Roo.bootstrap.BezierSignature
44876 * @extends Roo.bootstrap.Component
44877 * Bootstrap BezierSignature class
44878 * This script refer to:
44879 * Title: Signature Pad
44881 * Availability: https://github.com/szimek/signature_pad
44884 * Create a new BezierSignature
44885 * @param {Object} config The config object
44888 Roo.bootstrap.BezierSignature = function(config){
44889 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44895 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44902 mouse_btn_down: true,
44905 * @cfg {int} canvas height
44907 canvas_height: '200px',
44910 * @cfg {float|function} Radius of a single dot.
44915 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44920 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44925 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44930 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44935 * @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.
44937 bg_color: 'rgba(0, 0, 0, 0)',
44940 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44942 dot_color: 'black',
44945 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44947 velocity_filter_weight: 0.7,
44950 * @cfg {function} Callback when stroke begin.
44955 * @cfg {function} Callback when stroke end.
44959 getAutoCreate : function()
44961 var cls = 'roo-signature column';
44964 cls += ' ' + this.cls;
44974 for(var i = 0; i < col_sizes.length; i++) {
44975 if(this[col_sizes[i]]) {
44976 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44986 cls: 'roo-signature-body',
44990 cls: 'roo-signature-body-canvas',
44991 height: this.canvas_height,
44992 width: this.canvas_width
44999 style: 'display: none'
45007 initEvents: function()
45009 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45011 var canvas = this.canvasEl();
45013 // mouse && touch event swapping...
45014 canvas.dom.style.touchAction = 'none';
45015 canvas.dom.style.msTouchAction = 'none';
45017 this.mouse_btn_down = false;
45018 canvas.on('mousedown', this._handleMouseDown, this);
45019 canvas.on('mousemove', this._handleMouseMove, this);
45020 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45022 if (window.PointerEvent) {
45023 canvas.on('pointerdown', this._handleMouseDown, this);
45024 canvas.on('pointermove', this._handleMouseMove, this);
45025 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45028 if ('ontouchstart' in window) {
45029 canvas.on('touchstart', this._handleTouchStart, this);
45030 canvas.on('touchmove', this._handleTouchMove, this);
45031 canvas.on('touchend', this._handleTouchEnd, this);
45034 Roo.EventManager.onWindowResize(this.resize, this, true);
45036 // file input event
45037 this.fileEl().on('change', this.uploadImage, this);
45044 resize: function(){
45046 var canvas = this.canvasEl().dom;
45047 var ctx = this.canvasElCtx();
45048 var img_data = false;
45050 if(canvas.width > 0) {
45051 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45053 // setting canvas width will clean img data
45056 var style = window.getComputedStyle ?
45057 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45059 var padding_left = parseInt(style.paddingLeft) || 0;
45060 var padding_right = parseInt(style.paddingRight) || 0;
45062 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45065 ctx.putImageData(img_data, 0, 0);
45069 _handleMouseDown: function(e)
45071 if (e.browserEvent.which === 1) {
45072 this.mouse_btn_down = true;
45073 this.strokeBegin(e);
45077 _handleMouseMove: function (e)
45079 if (this.mouse_btn_down) {
45080 this.strokeMoveUpdate(e);
45084 _handleMouseUp: function (e)
45086 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45087 this.mouse_btn_down = false;
45092 _handleTouchStart: function (e) {
45094 e.preventDefault();
45095 if (e.browserEvent.targetTouches.length === 1) {
45096 // var touch = e.browserEvent.changedTouches[0];
45097 // this.strokeBegin(touch);
45099 this.strokeBegin(e); // assume e catching the correct xy...
45103 _handleTouchMove: function (e) {
45104 e.preventDefault();
45105 // var touch = event.targetTouches[0];
45106 // _this._strokeMoveUpdate(touch);
45107 this.strokeMoveUpdate(e);
45110 _handleTouchEnd: function (e) {
45111 var wasCanvasTouched = e.target === this.canvasEl().dom;
45112 if (wasCanvasTouched) {
45113 e.preventDefault();
45114 // var touch = event.changedTouches[0];
45115 // _this._strokeEnd(touch);
45120 reset: function () {
45121 this._lastPoints = [];
45122 this._lastVelocity = 0;
45123 this._lastWidth = (this.min_width + this.max_width) / 2;
45124 this.canvasElCtx().fillStyle = this.dot_color;
45127 strokeMoveUpdate: function(e)
45129 this.strokeUpdate(e);
45131 if (this.throttle) {
45132 this.throttleStroke(this.strokeUpdate, this.throttle);
45135 this.strokeUpdate(e);
45139 strokeBegin: function(e)
45141 var newPointGroup = {
45142 color: this.dot_color,
45146 if (typeof this.onBegin === 'function') {
45150 this.curve_data.push(newPointGroup);
45152 this.strokeUpdate(e);
45155 strokeUpdate: function(e)
45157 var rect = this.canvasEl().dom.getBoundingClientRect();
45158 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45159 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45160 var lastPoints = lastPointGroup.points;
45161 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45162 var isLastPointTooClose = lastPoint
45163 ? point.distanceTo(lastPoint) <= this.min_distance
45165 var color = lastPointGroup.color;
45166 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45167 var curve = this.addPoint(point);
45169 this.drawDot({color: color, point: point});
45172 this.drawCurve({color: color, curve: curve});
45182 strokeEnd: function(e)
45184 this.strokeUpdate(e);
45185 if (typeof this.onEnd === 'function') {
45190 addPoint: function (point) {
45191 var _lastPoints = this._lastPoints;
45192 _lastPoints.push(point);
45193 if (_lastPoints.length > 2) {
45194 if (_lastPoints.length === 3) {
45195 _lastPoints.unshift(_lastPoints[0]);
45197 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45198 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45199 _lastPoints.shift();
45205 calculateCurveWidths: function (startPoint, endPoint) {
45206 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45207 (1 - this.velocity_filter_weight) * this._lastVelocity;
45209 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45212 start: this._lastWidth
45215 this._lastVelocity = velocity;
45216 this._lastWidth = newWidth;
45220 drawDot: function (_a) {
45221 var color = _a.color, point = _a.point;
45222 var ctx = this.canvasElCtx();
45223 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45225 this.drawCurveSegment(point.x, point.y, width);
45227 ctx.fillStyle = color;
45231 drawCurve: function (_a) {
45232 var color = _a.color, curve = _a.curve;
45233 var ctx = this.canvasElCtx();
45234 var widthDelta = curve.endWidth - curve.startWidth;
45235 var drawSteps = Math.floor(curve.length()) * 2;
45237 ctx.fillStyle = color;
45238 for (var i = 0; i < drawSteps; i += 1) {
45239 var t = i / drawSteps;
45245 var x = uuu * curve.startPoint.x;
45246 x += 3 * uu * t * curve.control1.x;
45247 x += 3 * u * tt * curve.control2.x;
45248 x += ttt * curve.endPoint.x;
45249 var y = uuu * curve.startPoint.y;
45250 y += 3 * uu * t * curve.control1.y;
45251 y += 3 * u * tt * curve.control2.y;
45252 y += ttt * curve.endPoint.y;
45253 var width = curve.startWidth + ttt * widthDelta;
45254 this.drawCurveSegment(x, y, width);
45260 drawCurveSegment: function (x, y, width) {
45261 var ctx = this.canvasElCtx();
45263 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45264 this.is_empty = false;
45269 var ctx = this.canvasElCtx();
45270 var canvas = this.canvasEl().dom;
45271 ctx.fillStyle = this.bg_color;
45272 ctx.clearRect(0, 0, canvas.width, canvas.height);
45273 ctx.fillRect(0, 0, canvas.width, canvas.height);
45274 this.curve_data = [];
45276 this.is_empty = true;
45281 return this.el.select('input',true).first();
45284 canvasEl: function()
45286 return this.el.select('canvas',true).first();
45289 canvasElCtx: function()
45291 return this.el.select('canvas',true).first().dom.getContext('2d');
45294 getImage: function(type)
45296 if(this.is_empty) {
45301 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45304 drawFromImage: function(img_src)
45306 var img = new Image();
45308 img.onload = function(){
45309 this.canvasElCtx().drawImage(img, 0, 0);
45314 this.is_empty = false;
45317 selectImage: function()
45319 this.fileEl().dom.click();
45322 uploadImage: function(e)
45324 var reader = new FileReader();
45326 reader.onload = function(e){
45327 var img = new Image();
45328 img.onload = function(){
45330 this.canvasElCtx().drawImage(img, 0, 0);
45332 img.src = e.target.result;
45335 reader.readAsDataURL(e.target.files[0]);
45338 // Bezier Point Constructor
45339 Point: (function () {
45340 function Point(x, y, time) {
45343 this.time = time || Date.now();
45345 Point.prototype.distanceTo = function (start) {
45346 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45348 Point.prototype.equals = function (other) {
45349 return this.x === other.x && this.y === other.y && this.time === other.time;
45351 Point.prototype.velocityFrom = function (start) {
45352 return this.time !== start.time
45353 ? this.distanceTo(start) / (this.time - start.time)
45360 // Bezier Constructor
45361 Bezier: (function () {
45362 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45363 this.startPoint = startPoint;
45364 this.control2 = control2;
45365 this.control1 = control1;
45366 this.endPoint = endPoint;
45367 this.startWidth = startWidth;
45368 this.endWidth = endWidth;
45370 Bezier.fromPoints = function (points, widths, scope) {
45371 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45372 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45373 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45375 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45376 var dx1 = s1.x - s2.x;
45377 var dy1 = s1.y - s2.y;
45378 var dx2 = s2.x - s3.x;
45379 var dy2 = s2.y - s3.y;
45380 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45381 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45382 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45383 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45384 var dxm = m1.x - m2.x;
45385 var dym = m1.y - m2.y;
45386 var k = l2 / (l1 + l2);
45387 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45388 var tx = s2.x - cm.x;
45389 var ty = s2.y - cm.y;
45391 c1: new scope.Point(m1.x + tx, m1.y + ty),
45392 c2: new scope.Point(m2.x + tx, m2.y + ty)
45395 Bezier.prototype.length = function () {
45400 for (var i = 0; i <= steps; i += 1) {
45402 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45403 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45405 var xdiff = cx - px;
45406 var ydiff = cy - py;
45407 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45414 Bezier.prototype.point = function (t, start, c1, c2, end) {
45415 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45416 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45417 + (3.0 * c2 * (1.0 - t) * t * t)
45418 + (end * t * t * t);
45423 throttleStroke: function(fn, wait) {
45424 if (wait === void 0) { wait = 250; }
45426 var timeout = null;
45430 var later = function () {
45431 previous = Date.now();
45433 result = fn.apply(storedContext, storedArgs);
45435 storedContext = null;
45439 return function wrapper() {
45441 for (var _i = 0; _i < arguments.length; _i++) {
45442 args[_i] = arguments[_i];
45444 var now = Date.now();
45445 var remaining = wait - (now - previous);
45446 storedContext = this;
45448 if (remaining <= 0 || remaining > wait) {
45450 clearTimeout(timeout);
45454 result = fn.apply(storedContext, storedArgs);
45456 storedContext = null;
45460 else if (!timeout) {
45461 timeout = window.setTimeout(later, remaining);